从Shell脚本中,如何检查目录是否包含文件?
与此类似
1
| if [ -e /some/dir/* ]; then echo"huzzah"; fi; |
但如果目录包含一个或多个文件,则该方法有效(以上一个仅适用于0或1个文件)。
三种最佳技巧
shopt -s nullglob dotglob; f=your/dir/*; ((${#f}))
这个技巧是100%bash并调用(产生)一个子shell。这个想法来自Bruno De Fraine,并根据teambob的评论进行了改进。
1 2 3 4 5 6 7
| files=$(shopt -s nullglob dotglob; echo your/dir/*)
if (( ${#files} ))
then
echo"contains files"
else
echo"empty (or does not exist or is a file)"
fi |
注意:空目录和不存在的目录(即使提供的路径是文件)之间也没有区别。
#bash IRC频道的"官方"常见问题解答中有类似的替代方法和更多详细信息(以及更多示例):
1 2 3 4 5 6
| if (shopt -s nullglob dotglob; f=(*); ((${#f[@]})))
then
echo"contains files"
else
echo"empty (or does not exist, or is a file)"
fi |
[ -n"$(ls -A your/dir)" ]
该技巧的灵感来自nixCraft在2007年发布的文章。添加2>/dev/null可抑制输出错误"No such file or directory"。
另请参阅安德鲁·泰勒(Andrew Taylor)的答案(2008)和gr8can8dian的答案(2011)。
1 2 3 4 5 6
| if [ -n"$(ls -A your/dir 2>/dev/null)" ]
then
echo"contains files (or is a file)"
else
echo"empty (or does not exist)"
fi |
或单行Bashism版本:
1
| [[ $(ls -A your/dir) ]] && echo"contains files" || echo"empty" |
注意:当目录不存在时,ls返回$?=2。但是文件和空目录之间没有区别。
[ -n"$(find your/dir -prune -empty)" ]
这最后一个技巧来自gravstar的答案,其中-maxdepth 0替换为-prune,并通过phils的注释进行了改进。
1 2 3 4 5 6
| if [ -n"$(find your/dir -prune -empty 2>/dev/null)" ]
then
echo"empty (directory or file)"
else
echo"contains files (or does not exist)"
fi |
使用-type d的变体:
1 2 3 4 5 6
| if [ -n"$(find your/dir -prune -empty -type d 2>/dev/null)" ]
then
echo"empty directory"
else
echo"contains files (or does not exist or is not a directory)"
fi |
说明:
-
find -prune与find -maxdepth 0类似,但使用的字符更少
-
find -empty打印空目录和文件
-
find -type d仅打印目录
注意:您也可以将[ -n"$(find your/dir -prune -empty)" ]替换为以下简短版本:
1 2 3 4 5 6
| if [ `find your/dir -prune -empty 2>/dev/null` ]
then
echo"empty (directory or file)"
else
echo"contains files (or does not exist)"
fi |
最后的代码在大多数情况下都适用,但是请注意,恶意路径可能会表达命令...
到目前为止,解决方案使用ls。这是一个全bash解决方案:
1 2 3 4
| #!/bin/bash
shopt -s nullglob dotglob # To include hidden files
files=(/some/dir/*)
if [ ${#files[@]} -gt 0 ]; then echo"huzzah"; fi |
怎么样:
1
| if find /some/dir/ -maxdepth 0 -empty | read v; then echo"Empty dir"; fi |
这样,就无需生成目录内容的完整列表。 read既可以丢弃输出,也可以使表达式仅在读取内容时才为true(即find发现/some/dir/为空)。
尝试:
1
| if [ ! -z `ls /some/dir/*` ]; then echo"huzzah"; fi |
注意包含大量文件的目录!评估ls命令可能需要一些时间。
IMO最好的解决方案是使用
1
| find /some/dir/ -maxdepth 0 -empty |
1 2 3 4 5 6 7 8 9 10 11 12
| # Works on hidden files, directories and regular files
### isEmpty()
# This function takes one parameter:
# $1 is the directory to check
# Echoes"huzzah" if the directory has files
function isEmpty(){
if ["$(ls -A $1)" ]; then
echo"huzzah"
else
echo"has no files"
fi
} |
1 2 3 4
| DIR="/some/dir"
if ["$(ls -A $DIR)" ]; then
echo 'There is something alive in here'
fi |
你能比较一下这个输出吗?
1
| ls -A /some/dir | wc -l |
这告诉我目录是否为空,如果不是,则为目录包含的文件数。
1 2 3 4 5 6 7 8
| directory="/some/dir"
number_of_files=$(ls -A $directory | wc -l)
if ["$number_of_files" =="0" ]; then
echo"directory $directory is empty"
else
echo"directory $directory contains $number_of_files files"
fi |
1 2 3 4 5 6 7 8 9 10 11 12
| # Checks whether a directory contains any nonhidden files.
#
# usage: if isempty"$HOME"; then echo"Welcome home"; fi
#
isempty() {
for _ief in $1/*; do
if [ -e"$_ief" ]; then
return 1
fi
done
return 0
} |
一些实施说明:
-
for循环避免了对外部ls进程的调用。仍然会一次读取所有目录条目。这只能通过编写明确使用readdir()的C程序来进行优化。
-
循环内的test -e捕获空目录的情况,在这种情况下,变量_ief将被分配值" somedir / *"。仅当该文件存在时,该函数才会返回" nonempty"
-
此功能将在所有POSIX实现中使用。但是请注意,Solaris / bin / sh不在该类别中。它的test实现不支持-e标志。
这可能是一个很晚的响应,但这是可行的解决方案。该行仅识别文件的存在!如果目录存在,它不会给您带来误报。
1 2 3
| if find /path/to/check/* -maxdepth 0 -type f | read
then echo"Files Exist"
fi |
ZSH
我知道这个问题被标为" bash"。但是,仅供参考,适用于zsh用户:
测试非空目录
要检查foo是否非空:
1
| $ for i in foo(NF) ; do ... ; done |
如果foo为非空,则将执行for块中的代码。
测试空目录
要检查foo是否为空:
1
| $ for i in foo(N/^F) ; do ... ; done |
如果foo为空,则将执行for块中的代码。
笔记
我们不需要引用上面的目录foo,但是如果需要,可以这样做:
1
| $ for i in 'some directory!'(NF) ; do ... ; done |
我们也可以测试多个对象,即使它不是目录:
1 2 3 4
| $ mkdir X # empty directory
$ touch f # regular file
$ for i in X(N/^F) f(N/^F) ; do echo $i ; done # echo empty directories
X |
任何不是目录的内容都将被忽略。
附加功能
由于我们在进行globe,所以我们可以使用任何globe(或大括号扩展):
1 2 3 4 5 6 7 8
| $ mkdir X X1 X2 Y Y1 Y2 Z
$ touch Xf # create regular file
$ touch X1/f # directory X1 is not empty
$ touch Y1/.f # directory Y1 is not empty
$ ls -F # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ for i in {X,Y}*(N/^F); do printf"$i"; done; echo # print empty directories
X X2 Y Y2 |
我们还可以检查放置在数组中的对象。使用上面的目录,例如:
1 2 3 4 5
| $ ls -F # list all objects
X/ X1/ X2/ Xf Y/ Y1/ Y2/ Z/
$ arr=(*) # place objects into array"arr"
$ for i in ${^arr}(N/^F); do printf"$i"; done; echo
X X2 Y Y2 Z |
因此,我们可以测试可能已经在数组参数中设置的对象。
注意,显然,for块中的代码依次在每个目录上执行。如果不希望这样,则可以简单地填充一个数组参数,然后对该参数进行操作:
1 2
| $ for i in *(NF) ; do full_directories+=($i) ; done
$ do_something $full_directories |
说明
对于zsh用户,有一个(F) glob限定符(请参阅man zshexpn),它与"完整"(非空)目录匹配:
1 2 3 4 5 6 7
| $ mkdir X Y
$ touch Y/.f # Y is now not empty
$ touch f # create a regular file
$ ls -dF * # list everything in the current directory
f X/ Y/
$ ls -dF *(F) # will list only"full" directories
Y/ |
限定符(F)列出匹配的对象:是一个目录,并且不为空。因此,(^F)匹配:不是目录,或者为空。因此,例如,单独的(^F)也会列出常规文件。因此,如zshexp手册页所述,我们还需要(/) glob限定符,该限定符仅列出目录:
1 2 3 4
| $ mkdir X Y Z
$ touch X/f Y/.f # directories X and Y now not empty
$ for i in *(/^F) ; do echo $i ; done
Z |
因此,要检查给定目录是否为空,可以运行:
1 2 3 4
| $ mkdir X
$ for i in X(/^F) ; do echo $i ; done ; echo"finished"
X
finished |
并确保不会捕获非空目录:
1 2 3 4 5
| $ mkdir Y
$ touch Y/.f
$ for i in Y(/^F) ; do echo $i ; done ; echo"finished"
zsh: no matches found: Y(/^F)
finished |
糟糕!由于Y不为空,因此zsh找不到与(/^F)匹配的内容("目录为空"),因此吐出一条错误消息,指出未找到与该glob匹配的内容。因此,我们需要使用(N) glob限定符抑制这些可能的错误消息:
1 2 3 4
| $ mkdir Y
$ touch Y/.f
$ for i in Y(N/^F) ; do echo $i ; done ; echo"finished"
finished |
因此,对于空目录,我们需要使用限定符(N/^F),您可以将其读取为:"不要警告我失败,目录不完整"。
同样,对于非空目录,我们需要使用限定符(NF),我们也可以将其理解为:"不要警告我失败,完整目录"。
1 2 3 4 5 6 7
| dir_is_empty() {
["${1##*/}" ="*" ]
}
if dir_is_empty /some/dir/* ; then
echo"huzzah"
fi |
假设您在/any/dir/you/check中没有名为*的文件,它应该可以在bash dash posh busybox sh和zsh上运行,但是(对于zsh)需要unsetopt nomatch。
性能应该可以与使用*(glob)的任何ls媲美,我想在具有许多节点的目录上速度会很慢(我的/usr/bin具有3000多个文件不会那么慢),将至少使用足够的内存来分配所有dirs /文件名(以及更多),因为它们都作为参数传递(解析)给函数,某些shell可能对参数数量和/或参数长度有限制。
一种可移植的快速O(1)零资源检查目录是否为空的方法将是不错的选择。
更新
如果需要进行更多测试,例如Rich的sh(POSIX shell)技巧中的is_empty,上述版本不考虑隐藏文件/目录。
1 2 3 4 5 6
| is_empty () (
cd"$1"
set -- .[!.]* ; test -f"$1" && return 1
set -- ..?* ; test -f"$1" && return 1
set -- * ; test -f"$1" && return 1
return 0 ) |
但是,我正在考虑这样的事情:
1 2 3
| dir_is_empty() {
["$(find"$1" -name"?*" | dd bs=$((${#1}+3)) count=1 2>/dev/null)" ="$1" ]
} |
令人担忧的是,当dir为空时,尾随斜杠与参数和find输出之间的差异以及尾随换行符(但这应该很容易处理)在我的busybox sh上令人遗憾地显示了< x14>管道,其输出被随机截断(如果我使用cat,则输出始终是相同的,似乎是带有参数count的dd)。
我很惊讶没有提到空目录上的wootedge指南。对于壳类型问题,本指南以及所有wooleedge都是必读的。
该页面值得注意的是:
Never try to parse ls output. Even ls -A solutions can break (e.g. on HP-UX, if you are root, ls -A does
the exact opposite of what it does if you're not root -- and no, I can't make up something that
incredibly stupid).
In fact, one may wish to avoid the direct question altogether. Usually people want to know whether a
directory is empty because they want to do something involving the files therein, etc. Look to the larger
question. For example, one of these find-based examples may be an appropriate solution:
1 2 3 4
| # Bourne
find"$somedir" -type f -exec echo Found unexpected file {} \\;
find"$somedir" -maxdepth 0 -empty -exec echo {} is empty. \\; # GNU/BSD
find"$somedir" -type d -empty -exec cp /my/configfile {} \\; # GNU/BSD |
Most commonly, all that's really needed is something like this:
1 2 3 4 5
| # Bourne
for f in ./*.mpg; do
test -f"$f" || continue
mympgviewer"$f"
done |
In other words, the person asking the question may have thought an explicit empty-directory test was
needed to avoid an error message like mympgviewer: ./*.mpg: No such file or directory when in fact no
such test is required.
从olibre的答案中得出一个(或几个)提示,我喜欢Bash函数:
1 2 3
| function isEmptyDir {
[ -d $1 -a -n"$( find $1 -prune -empty 2>/dev/null )" ]
} |
因为尽管它创建了一个子外壳,但正如我所想象的那样,它与O(1)解决方案非常接近,并为其命名可以使其可读。然后我可以写
1 2 3 4 5 6
| if isEmptyDir somedir
then
echo somedir is an empty directory
else
echo somedir does not exist, is not a dir, is unreadable, or is not empty
fi |
至于O(1),则存在异常情况:如果大型目录已删除或删除了除最后一个条目以外的所有条目,则"查找"可能必须阅读整个内容以确定它是否为空。我相信预期性能为O(1),但最坏情况是目录大小呈线性关系。我没有测量。
布鲁诺答案的小变化:
1 2 3 4 5 6 7
| files=$(ls -1 /some/dir| wc -l)
if [ $files -gt 0 ]
then
echo"Contains files"
else
echo"Empty"
fi |
这个对我有用
到目前为止,我还没有看到使用grep的答案,我认为这会给出一个更简单的答案(没有太多怪异的符号!)。我会这样
使用bourne shell检查目录中是否存在任何文件:
这将返回目录中的文件数:
1
| ls -l <directory> | egrep -c"^-" |
您可以填写写入目录的目录路径。管道的前半部分确保每个文件的输出第一个字符为"-"。然后egrep计算以该行开头的行数
使用正则表达式的符号。现在,您要做的就是存储获得的数字,并使用反引号将其进行比较,例如:
1 2 3 4 5 6
| #!/bin/sh
fileNum=`ls -l <directory> | egrep -c"^-"`
if [ $fileNum == x ]
then
#do what you want to do
fi |
x是您选择的变量。
混合修剪的东西和最后的答案,我到
1
| find"$some_dir" -prune -empty -type d | read && echo empty || echo"not empty" |
也适用于带有空格的路径
用bash的简单答案:
1
| if [[ $(ls /some/dir/) ]]; then echo"huzzah"; fi; |
我会选择find:
1 2 3 4
| if [ -z"$(find $dir -maxdepth 1 -type f)" ]; then
echo"$dir has NO files"
else
echo"$dir has files" |
这将检查仅在目录中查找文件的输出,而无需遍历子目录。然后,它使用从man test提取的-z选项检查输出:
1 2
| -z STRING
the length of STRING is zero |
看到一些结果:
1 2
| $ mkdir aaa
$ dir="aaa" |
空目录:
1 2
| $ [ -z"$(find aaa/ -maxdepth 1 -type f)" ] && echo"empty"
empty |
只是在其中:
1 2 3
| $ mkdir aaa/bbb
$ [ -z"$(find aaa/ -maxdepth 1 -type f)" ] && echo"empty"
empty |
目录中的文件:
1 2 3
| $ touch aaa/myfile
$ [ -z"$(find aaa/ -maxdepth 1 -type f)" ] && echo"empty"
$ rm aaa/myfile |
子目录中的文件:
1 2 3
| $ touch aaa/bbb/another_file
$ [ -z"$(find aaa/ -maxdepth 1 -type f)" ] && echo"empty"
empty |
通过一些变通办法,我可以找到一种简单的方法来查找目录中是否有文件。使用grep命令可以进一步扩展此功能,以专门检查.xml或.txt文件等。例如:ls /some/dir | grep xml | wc -l | grep -w"0"
1 2 3 4 5 6 7
| #!/bin/bash
if ([ $(ls /some/dir | wc -l | grep -w"0") ])
then
echo 'No files'
else
echo 'Found files'
fi |
1
| if ls /some/dir/* >/dev/null 2>&1 ; then echo"huzzah"; fi; |
尝试使用命令查找。
指定目录为硬编码或作为参数。
然后启动查找以搜索目录中的所有文件。
检查查找的返回是否为空。
回显查找的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #!/bin/bash
_DIR="/home/user/test/"
#_DIR=$1
_FIND=$(find $_DIR -type f )
if [ -n"$_FIND" ]
then
echo -e"$_DIR contains files or subdirs with files \
\
"
echo"$_FIND"
else
echo"empty (or does not exist)"
fi |
测试特定的目标目录
1 2 3 4 5 6 7 8 9 10
| if [ -d $target_dir ]; then
ls_contents=$(ls -1 $target_dir | xargs);
if [ ! -z"$ls_contents" -a"$ls_contents" !="" ]; then
echo"is not empty";
else
echo"is empty";
fi;
else
echo"directory does not exist";
fi; |
我不喜欢发布的ls - A解决方案。您很可能希望测试目录是否为空,因为您不想将其删除。下面这样做。但是,如果您只想记录一个空文件,那么确定删除并重新创建它会更快,然后列出可能无限的文件?
这应该工作...
1 2 3 4 5 6 7
| if ! rmdir ${target}
then
echo"not empty"
else
echo"empty"
mkdir ${target}
fi |
这对我来说很好(当目录存在时):
1 2
| some_dir="/some/dir with whitespace & other characters/"
if find"`echo"$some_dir"`" -maxdepth 0 -empty | read v; then echo"Empty dir"; fi |
经过全面检查:
1 2 3
| if [ -d"$some_dir" ]; then
if find"`echo"$some_dir"`" -maxdepth 0 -empty | read v; then echo"Empty dir"; else"Dir is NOT empty" fi
fi |