我需要检查所有子目录并报告它们包含多少个文件(无需进一步递归):
directoryName1 numberOfFiles
directoryName2 numberOfFiles
我需要检查所有子目录并报告它们包含多少个文件(无需进一步递归):
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Answers:
这是以安全且可移植的方式完成的。它不会被奇怪的文件名所迷惑。
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
请注意,它将首先打印文件数量,然后在单独的行上打印目录名称。如果您希望保留OP的格式,则需要进一步的格式化,例如
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
如果您有一组特定的子目录感兴趣,则可以将其替换*
。
为什么这样安全?(因此值得编写脚本)
文件名可以包含除以外的任何字符/
。Shell或命令对一些字符进行了特殊处理。这些包括空格,换行符和破折号。
使用for f in *
构造是获取每个文件名的安全方法,无论文件名包含什么。
将文件名包含在变量中后,您仍然必须避免使用find $f
。如果$f
包含文件名-test
,find
则会抱怨您刚刚提供的选项。避免这种情况的方法./
是在名称前使用;这样,它具有相同的含义,但不再以短划线开头。
换行符和空格也是一个问题。如果$f
包含“ hello,buddy”作为文件名find ./$f
,则为find ./hello, buddy
。您要告诉find
您查看./hello,
和buddy
。如果不存在,它将抱怨,并且永远不会进入./hello, buddy
。这很容易避免-在变量周围使用引号。
最后,文件名可以包含换行符,因此无法计算文件名列表中的换行符;您会使用换行符为每个文件名获得额外的计数。为避免这种情况,请不要在文件列表中计算换行符;相反,计算代表单个文件的换行符(或其他任何字符)。这就是find
命令简单-exec echo \;
而没有的原因-exec echo {} \;
。我只想打印一个新行来计算文件。
-mindepth 1
-printf '\n'
代替-exec echo
。
-printf
则可以,但是如果您希望它在FreeBSD上运行,则不可以。
假设您正在寻找一个标准的Linux解决方案,一个相对简单的方法是find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
在find
遍历两个指定的子目录的地方,到-maxdepth
的1 中将阻止进一步的递归,并且仅报告-type f
用换行符分隔的文件()。然后将结果通过管道传递wc
以计数那些行的数量。
find . -maxdepth 1 -type d
输出合并?
find $dirs ...
或者(b)如果它们仅位于一个更高级别的目录中,则该目录中的globfind */ ...
-exec echo
到您的find命令-这样,它就不会回显文件名,而只是换行。
“无递归”是指如果directoryName1
具有子目录,那么您不想计算子目录中的文件吗?如果是这样,这是一种对指定目录中的所有常规文件进行计数的方法:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
请注意,该-f
测试执行两个功能:测试与上述glob之一匹配的条目是否为常规文件,并且测试该条目是否为匹配项(如果其中一个glob不匹配,则模式保持不变¹)。如果要计算给定目录中的所有条目,而不论其类型如何,请替换-f
为-e
。
Ksh可以使模式与点文件匹配,并在没有文件与模式匹配的情况下生成空列表。因此,在ksh中,您可以像这样计数常规文件:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
或所有文件,如下所示:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
Bash有多种方法可以简化此过程。要计算常规文件:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
要计算所有文件:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
像往常一样,在zsh中它甚至更简单。要计算常规文件:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
更改(DN.)
为(DN)
计算所有文件。
¹ 请注意,每个模式都匹配,否则结果可能会不正确(例如,如果您要计算以数字开头的文件,则不能仅仅这样做,for x in [0-9]*; do if [ -f "$x" ]; then …
因为可能有一个名为的文件[0-9]foo
)。
基于计数脚本,Shawn的答案和Bash技巧,可以确保即使使用换行符的文件名也以可用形式打印在一行上:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %q
是打印带引号的字符串版本,即单行字符串,您可以将其放入Bash脚本中,以解释为包含(可能是)换行符和其他特殊字符的文字字符串。例如,看到echo -n $'\tfoo\nbar'
VS printf %q $'\tfoo\nbar'
。
该find
命令的工作原理是简单地为每个文件打印一个字符,然后对这些字符进行计数而不是对行进行计数。
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
在Bash会用时使用?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
:对于所有目录,计算该目录中的条目数(包括隐藏的点文件,不包括.
和..
)