【问题标题】:Bash command to see if any files in dir - test if a directory is empty [duplicate]Bash命令查看目录中是否有任何文件 - 测试目录是否为空[重复]
【发布时间】:2016-02-09 01:45:16
【问题描述】:

我有以下 bash 脚本:

if ls /Users/david/Desktop/empty > /dev/null
then
    echo 'yes -- files'
else
    echo 'no -- files'
fi

如果/Users/david/Desktop/empty 目录中有一个或多个文件,我将如何修改第一行以使其评估为真?

【问题讨论】:

  • 您想要统计文件、文件和目录,包括/排除隐藏的dot 文件/目录吗?
  • @StephenP 当然,如果目录是非空的,任何可以告诉我的东西。
  • 我建议重新审视可用的答案。有几个不错的(来自 mklement0 和 anubhava 的和我自己的一样好)。
  • 请注意,该问题使用了更广泛意义上的“文件”一词,即问题中任何包含的文件系统项 - 即,一个通用的emptiness 需要测试(从接受的答案推断)。
  • @David542 — 我想删除我的答案,因为 globbing 解决方案更好,但当它是公认的答案时我无法删除它。你能选择其他东西作为接受吗?谢谢。

标签: bash shell unix


【解决方案1】:

这在BashFAQ #004 中有详细介绍。值得注意的是,use of ls for this purpose is an antipattern and should be avoided

shopt -s dotglob   # if including hidden files is desired
files=( "$dir"/* )
[[ -e $files || -L $files ]] && echo "Directory is not empty"

[[ -e $files ]] 实际上并不检查整个数组的内容是否存在;相反,它会检查返回的名字——当没有文件匹配时,它会处理这种情况,其中 glob 表达式本身作为唯一的结果返回。


值得注意的是:

  • 这比调用ls快得多,后者需要使用fork() 生成子shell,execve()/bin/ls 替换该子shell,操作系统的动态链接器要加载ls 二进制文件等使用的共享库 [一个例外是非常大的目录,有数万个文件——在这种情况下ls 也会很慢;请参阅下面的基于find 的解决方案]。
  • 这比调用ls更正确:通过globbing 返回的文件列表保证与文件的文字名称完全匹配,而ls 可以使用隐藏字符替换名称。如果第一个条目是一个有效的文件名,"${files[@]}" 可以安全地迭代并确保每个返回的值都是一个名称,并且如果本地 @987654336 @ 实现不会逃避它们。

也就是说,另一种方法是使用find,如果您有一个带有-empty 扩展名的方法(可从GNU find 和包括Mac OS 在内的现代BSD 中获得):

[[ $(find -H "$dir" -maxdepth 0 -type d -empty) ]] || echo "Directory is not empty"

...如果 any 结果给出,则目录非空。虽然比对不是异常大的目录进行通配要慢,但比 eitherls 或对目录缓存中不存在的超大目录进行通配要快,因为它可以在不进行完整扫描的情况下返回结果。

【讨论】:

    【解决方案2】:

    强大的纯 Bash 解决方案:

    有关为什么纯 Bash 解决方案优于使用 ls 的背景知识,请参阅 Charles Duffy's helpful answer,其中还包含一个基于find 的替代方案,它速度快得多,并且使用大型目录占用的内存更少。[1]
    还要考虑 anubhava 的同样快速且内存效率高的 stat-based answer,但是,它在 Linux 和 BSD/OSX 上需要不同的语法形式。

    更新为更简单的解决方案,感谢改编自this answer

    # EXCLUDING hidden files and folders - note the *quoted* use of glob '*'
    if compgen -G '*' >/dev/null; then
      echo 'not empty'
    else
      echo 'empty, but may have hidden files/dirs.'
    fi
    
    • compgen -G 通常用于制表符补全,但在这种情况下也很有用:

      • 请注意,compgen -G 进行 自己的 globbing,因此您必须将 glob(文件名模式)用引号传递给它,以便它输出所有匹配项。在这种特殊情况下,即使预先传递 unquoted 模式也是可行的,但差异一文不值。

      • 如果没有匹配,compgen -G always 产生 no 输出(与nullglob 选项的状态无关),并通过其退出指示编码是否找到至少 1 个匹配项,这是条件所利用的(同时使用 >/dev/null 抑制任何标准输出输出)。

    # INCLUDING hidden files and folders - note the *unquoted* use of glob *
    if (shopt -s dotglob; compgen -G * >/dev/null); then
      echo 'not empty'
    else
      echo 'completely empty'
    fi
    
    • compgen -G never 匹配 hidden 项(无论dotglob 选项的状态如何),因此也需要一种解决方法来查找隐藏项:

      • (...) 为条件创建一个子shell;也就是说,子shell中执行的命令不会影响当前shell的环境,这让我们可以本地化设置dotglob选项。

      • shopt -s dotglob 导致 * 也匹配隐藏项(... 除外)。

      • compgen -G * with unquoted *,感谢 shell 的 预先 扩展,至少传递了一个文件名,无论是隐藏的还是not(忽略其他文件名)或空字符串,如果既不存在隐藏项也不存在非隐藏项。在前一种情况下,退出代码是0(表示成功,因此是一个非空目录),在后一种情况下是1(表示一个真正的空目录)。


    [1] 这个答案最初错误地声称基于以下方法提供了一个仅适用于大型目录的 Bash 解决方案:(shopt -s nullglob dotglob; for f in "$dir"/*; do exit 0; done; exit 1)。 这不是更有效,因为在内部,Bash still 在进入循环之前首先收集数组中的所有匹配项 - 换句话说:for *not 懒惰地评估的。

    【讨论】:

    • 这是一个很好的答案,@CharlesDuffy ++ 对这两个答案的回答也是如此。我总是通过阅读这些出色的解决方案来学习一些东西。然而奇怪的是,OP 标记了一个所谓的错误答案。
    【解决方案3】:

    这是一个基于stat 命令的解决方案,如果针对目录(或指向目录的链接)运行,该命令可以返回硬链接数。它从 3 开始增加 硬链接数,因为前两个是 ... 条目,因此从这个数字中减去 2 给出给定目录中的实际条目数(这包括符号链接以及)。

    所以把它们放在一起:

    (( ($(stat -Lc '%h' "$dir") - 2) > 0)) && echo 'not empty' || echo 'empty'
    

    根据man stat 使用的选项是:

    %h     number of hard links
    -L     --dereference, follow links
    

    编辑:要使其与 BSD/OSX 兼容,请使用:

    (( ($(stat -Lf '%l' "$dir") - 2) > 0)) && echo 'not empty' || echo 'empty'
    

    【讨论】:

    • 需要 GNU stat,但是一个非常好的选择——在大目录中应该和 find -empty 一样快。
    • 这很有趣,在 Linux Mint 17.1 下,使用 stat (GNU coreutils) 8.21,如果$dir 仅包含 filescode 对我不起作用/i> 并且没有目录,而当$dir 仅包含文件 和或目录时,mklement0 的代码 在 OS X 中工作我>。我非常尊重你投反对票只是因为它在我的测试用例中不起作用但是 IMO 这确实不能回答 OP 的问题,因为当 code 时他确实说“目录中的任何文件”如果$dir 中只有文件且没有目录,i> 可能会有条件地失败。还是我在这里完全错过了什么?
    • 感谢您的重要评论,stat 在不同文件系统上的行为似乎不同。我在 OSX 上,它可以识别给定子目录中的任何文件/目录/链接。将尝试访问 Linux 主机以进行进一步测试。
    猜你喜欢
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2014-03-01
    • 2011-04-17
    • 2015-07-24
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    相关资源
    最近更新 更多