bash:计算目录数量

bash:计算目录数量

作为我的 bash 例程的一部分,我试图找到位于目录 $storage 中的子目录数并将其与某个变量关联,这些变量将在同一脚本中使用

number_dirs=$(ls -ld "${storage}"/* | wc -l)
  printf >&2 '%s is the number of the directories... ' "${number_dirs}" ;sleep 0.2
  printf >&2 "Keep calm!\n"

如果目录数量在 2-4K 左右,此方法可以正常工作,但如果目录数量很大,则无法正常工作。我如何以相同的方式使用 find 命令?

答案1

简单的扫描如下find

number_dirs=$(find ${storage} -maxdepth 1 -mindepth 1 -type d | wc -l)

答案2

# store all the dir names in an array
dirs=( "${storage}"/*/ )

num_dirs=${#dirs[@]}

全局模式中的尾部斜杠将结果限制为仅限目录。

答案3

分析

请注意,您的ls -ld "${storage}"/* | wc -l命令并不局限于目录;即,ls还将列出非目录$storage,并wc对其进行计数。考虑"${storage}"/*/(注意结尾的斜杠)将列出目录和指向目录的符号链接,除非没有匹配项。如果没有匹配项,则该/*/部分将保持文字。研究一下要做什么shopt -s failglob,然后shopt -s nullglob去做。

您的命令没有列出更深的子目录,我认为这种行为就是您想要的。

您的命令通常不会列出隐藏目录(名称以点开头)。如果您对它们感兴趣,请研究shopt -s dotglob

如果"${storage}"/*/扩展到大量单词,那么您将得到argument list too long。这是因为ls是一个外部可执行文件,需要使用参数数组调用,并且这是有限制的


Shell 可以执行此操作

你可以让 shell 为你计算目录数量:

(shopt -s nullglob && set -- "${storage}"/*/ && echo "$#")

set --将所有名称存储为位置参数,然后echo "$#"打印数字。set是内置函数,因此不应受到影响argument list too long。我故意使用了子 shell,因此当前 shell 的位置参数不受影响。我可以使用单独的数组(如另一个答案这样做确实可行),但存在一个潜在的问题。Bash 允许您设置大量位置参数。一方面,这很好,因为您想要计算大量目录。另一方面,即使您只想要计算它们的数量,参数也需要以字符串的形式存储在内存中。

请注意如果你这样做:

number_dirs=$(shopt -s nullglob && set -- "${storage}"/*/ && echo "$#")

那么 的内部$()无论如何都会在子 shell 中执行,因此您不需要额外的括号。您可以选择在子 shell 中使用数组。无论如何,子 shell 都会立即退出,echo并且内存将尽快释放。但是如果您这样做:

dirs=( "${storage}"/*/ )
num_dirs=${#dirs[@]}

如果不这样做unset dirs,数组就会成为负担。立即终止并释放内存的子 shell 比您可能忘记取消设置的消耗内存的数组要好。

无论如何,在计算所有扩展单词的数量之前,都需要先将其存储起来(即使只是暂时存储在子 shell 中)。因此,您可能更喜欢find允许wc动态计算。


或者find+wc

一个基本的解决方案是另一个答案重点是-type d。这个想法(与正确引用):

find "$storage" -maxdepth 1 -mindepth 1 -type d | wc -l

可能会失败,因为:

  • -maxdepth-mindepth不可移植;
  • 目录名称可能包含换行符,这会导致wc -l计数错误。

如果您的find支持-printf(因此它很可能支持-maxdepth-mindepth),那么您可以通过打印单个字节并对其进行计数来解决后一个问题:

find "$storage" -maxdepth 1 -mindepth 1 -type d -printf a | wc -c

其中a是任意单字节字符。

一个可移植的(但速度稍慢)解决方案是这样的:

( cd -- "$storage"/ && find . -type d ! -name . -prune -exec sh -c '
    for i do printf a; done
' find-sh {} + | wc -c )

子 shell 不会cd影响当前 shell 的当前工作目录。我使用first 是因为它允许我稍后cd -- "$storage"/引用,因此可以轻松将其排除而无需修剪。如果没有,我将需要使用类似 的东西,但 then会被解释为一个模式;所以这通常会失败。$storage.cd! -path "$storage"find$storage

另一方面,如果你被允许r读取目录但不允许执行xcd进入,请参阅这个答案) 则cd不会起作用,而没有的解决方案则cd可能。


笔记

  • cd -- "$storage"/里面双划线并且结尾的斜杠使路径看起来像-foo甚至-被解释为实际的路径名。

  • find-sh解释如下:中的第二个 sh 是什么sh -c 'some shell code' sh

  • 如果不想计算隐藏目录,请! -name '.*'在后添加-prune

  • -type d不匹配目录的符号链接。相比之下:shell 通配符模式类似于*/匹配它们。

  • 如果您想要进入子目录并计算整个子树,请省略-prune

  • shopt不可移植。我不知道在纯 Bash 中如何直接执行此sh操作shopt -s nullglob。您的问题已标记为,所以这应该不是问题。

相关内容