【问题标题】:Get the newest directory to a variable in Bash获取 Bash 中变量的最新目录
【发布时间】:2012-03-05 18:23:44
【问题描述】:

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

类似这样的:

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

谁能帮忙?

【问题讨论】:

    标签: bash


    【解决方案1】:
    BACKUPDIR=$(ls -td /backups/*/ | head -1)
    

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

    【讨论】:

    • 尽管被接受并得到了广泛的支持,但这个解决方案存在几个问题。如果目录中的最新项不是目录,则它不起作用。如果最新的子目录的名称以“.”开头,则它不起作用。如果最新的子目录的名称包含换行符,则它不起作用。 Shellcheck 抱怨使用 ls。有关处理ls 的输出的危险的详细说明,请参阅ParsingLs - Greg's Wiki
    • 如果备份中没有目录也会失败
    【解决方案2】:

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

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

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

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

    结果是出现了 test.txt 而不是最后修改的目录。

    建议的解决方案“有效”,但仅适用于最佳情况。

    假设您最多有 1 个目录深度,更好的解决方案是使用:

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

    只需将“/backups/”部分换成您的实际路径即可。

    如果你想避免在 bash 脚本中显示绝对路径,你总是可以使用这样的:

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

    【讨论】:

    • 尽管有赞成票,但此解决方案不起作用。 find /backups/* -type d -prune -exec ... 在 shell 扩展 /backups/* 时按照目录的生成顺序处理目录。顺序由名称决定,而不是时间戳。最后列出的目录通常不是是最新的。
    【解决方案3】:

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

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

    解释为什么这会好一点:

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

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

    【讨论】:

    • 对我来说,这不是对文件夹进行排序。
    • 这是因为它遇到了与this answer 上的 cmets 中描述的相同的问题。
    【解决方案4】:

    这是一个纯 Bash 解决方案:

    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\n' "$BACKUPDIR"
    

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

    【讨论】:

    • 这是唯一不会随意破坏有效文件的解决方案。
    【解决方案5】:

    有一个简单的解决方案,只使用ls

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

    在找到Listing only directories using ls in bash: An examination 之前,我不知道*/

    【讨论】:

    • 回答 pjh 对已接受答案的评论“如果最新目录以 '.' 开头则不起作用”,这也与我的回答有关。如果要包含以 . (但排除 ./ 和 ../)您可以将 ls 更改为: ls -td /backups/{.[^.],}?*/
    • 这主要是我自己的参考。使用 --quoting-style=shell-escape 似乎可以解决ParsingLs - Greg's Wiki 提出的许多 ls 问题
    【解决方案6】:

    你的“像这样的东西”几乎很受欢迎:

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

    把你写的和我学到的结合起来也解决了我的问题。感谢您提出这个问题。

    注意:我在 Windows 环境中的 GitBash 中运行上面的行,文件名为 ./something.bash

    【讨论】:

      【解决方案7】:

      使用 GNU find 您可以获得带有修改时间戳的目录列表,对该列表进行排序并输出最新的:

      find . -mindepth 1 -maxdepth 1 -type d -printf "%T@\t%p\0" | sort -z -n | cut -z -f2- | tail -z -n1
      

      或换行符分隔

      find . -mindepth 1 -maxdepth 1 -type d -printf "%T@\t%p\n" | sort -n | cut -f2- | tail -n1
      

      使用 POSIX 查找(没有-printf),如果有,您可以运行stat 以获取文件修改时间戳:

      find . -mindepth 1 -maxdepth 1 -type d -exec stat -c '%Y %n' {} \; | sort -n | cut -d' ' -f2- | tail -n1
      

      如果没有stat,则可以使用纯shell 解决方案,方法是将[[ bash 扩展替换为[,如this answer

      【讨论】:

        猜你喜欢
        • 2019-04-14
        • 1970-01-01
        • 1970-01-01
        • 2013-04-18
        • 1970-01-01
        • 2021-10-30
        • 2013-02-09
        • 1970-01-01
        相关资源
        最近更新 更多