作为我的 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
读取目录但不允许执行x
(cd
进入,请参阅这个答案) 则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
。您的问题已标记为狂欢,所以这应该不是问题。