获取bash中最新的变量目录

Get the newest directory in bash to a variables

我想在一个目录中找到最新的子目录,并将结果保存到bash中的变量中。

像这样:

1
ls -t /backups | head -1 > $BACKUPDIR

有人能帮忙吗?


1
BACKUPDIR=$(ls -t /backups | head -1)

$(...)在子shell中计算语句并返回输出。


仅使用ls有一个简单的解决方案:

1
BACKUPDIR=$(ls -td /backups/*/ | head -1)
  • -t按时间排序的订单(最新的第一个)
  • -d只列出此文件夹中的项目
  • */只列出目录
  • head -1返回第一项

直到我发现只在bash中使用ls列出目录时,我才知道*/:一个检查。


上面的解决方案没有考虑到文件被写入和从目录中删除,从而导致返回上目录而不是最新的子目录。

另一个问题是,此解决方案假定目录只包含其他目录,而不包含正在写入的文件。

假设我创建了一个名为"test.txt"的文件,然后再次运行此命令:

1
2
3
echo"test"> test.txt
ls -t /backups | head -1
test.txt

结果显示test.txt而不是最后修改的目录。

建议的解决方案"有效",但仅在最佳情况下有效。

假设最大目录深度为1,则更好的解决方案是使用:

1
find /backups/* -type d -prune -exec ls -d {} \; |tail -1

只需将"/backups/"部分替换为实际路径。

如果希望避免在bash脚本中显示绝对路径,则可以始终使用如下内容:

1
2
LOCALPATH=/backups
DIRECTORY=$(cd $LOCALPATH; find * -type d -prune -exec ls -d {} \; |tail -1)


嗯,我认为这个解决方案是最有效的:

1
2
path="/my/dir/structure/*"
backupdir=$(find $path -type d -prune | tail -n 1)

解释为什么会更好一点:

我们不需要子shell(除了将结果放入bash变量的shell)。我们不需要一个无用的-exec ls -dfind命令的末尾,它已经打印了目录列表。我们可以很容易地改变这一点,例如排除某些模式。例如,如果您想要第二个最新的目录,因为备份文件首先以相同的路径写入tmp目录:

1
backupdir=$(find $path -type -d -prune -not -name"*temp_dir" | tail -n 1)


这是一个纯bash解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
topdir=/backups
BACKUPDIR=

# Handle subdirectories beginning with '.', and empty $topdir
shopt -s dotglob nullglob

for file in"$topdir"/* ; do
    [[ -L $file || ! -d $file ]] && continue
    [[ -z $BACKUPDIR || $file -nt $BACKUPDIR ]] && BACKUPDIR=$file
done

printf 'BACKUPDIR=%q
'
"$BACKUPDIR"

它跳过符号链接,包括指向目录的符号链接,这可能是正确的做法,也可能不是正确的做法。它跳过其他非目录。它处理名称包含任何字符的目录,包括换行符和前导点。


要使用ls -t获取最新的文件夹,如果目录中没有目录,则可能需要区分文件和文件夹。使用一个简单的循环,您将得到一个安全、快速的结果,并且允许在将来轻松地实现不同的过滤器:

1
while read i ; do if [ -d"${i}" ] ; then newestFolder="${i}" ; break ; fi ; done < <(ls -t)

详细说明的块:

1
2
3
4
5
6
7
8
9
10
while read currentItemOnLoop # While reading each line of the file
do
  if [ -d"${currentItemOnLoop}" ] # If the item is a folder
  then
    newestFolder="${currentItemOnLoop}" # Then save it into the"newestFolder" variable
    break # and stop the loop
  else
    continue # Look for the next newest item
  fi
done < <(ls -t) # Sending the result of"ls -t" as a"file" to the"while read" loop

注意我精心设计的模块上的continue逻辑:

1
2
else
  continue # Look for the next newest item

你不会用的。我把它放在那里只是为了你的可见性,因为在这种情况下,它不会影响结果。