从 shell 脚本检查目录是否包含文件

从 shell 脚本中,如何检查目录是否包含文件?

和这个差不多

if [ -e /some/dir/* ]; then echo "huzzah"; fi;

但是如果目录包含一个或多个文件(上面的那个只适用于正好0个或1个文件)。

240405 次浏览

试试:

if [ ! -z `ls /some/dir/*` ]; then echo "huzzah"; fi
DIR="/some/dir"
if [ "$(ls -A $DIR)" ]; then
echo 'There is something alive in here'
fi

你能比较一下这个的输出吗?

 ls -A /some/dir | wc -l
if ls /some/dir/* >/dev/null 2>&1 ; then echo "huzzah"; fi;

到目前为止的解决方案都使用 ls:

#!/bin/bash
shopt -s nullglob dotglob     # To include hidden files
files=(/some/dir/*)
if [ ${#files[@]} -gt 0 ]; then echo "huzzah"; fi

下面这些怎么样:

if find /some/dir/ -maxdepth 0 -empty | read v; then echo "Empty dir"; fi

这样就不需要生成目录内容的完整列表。read既可以丢弃输出,也可以使表达式仅在某些内容被读取时计算为 true (即 /some/dir/find发现为空)。

注意有大量文件的目录! 评估 ls命令可能需要一些时间。

IMO 最好的解决方案是使用

find /some/dir/ -maxdepth 0 -empty

这可能是一个非常晚的反应,但这里有一个解决方案是有效的。这一行只能识别文件的存在!如果目录存在,它不会给你一个假阳性。

if find /path/to/check/* -maxdepth 0 -type f | read
then echo "Files Exist"
fi
# 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将被赋值为“ somdir/*”。只有当该文件存在时,函数才返回“ nonvoid”
  • 此函数将在所有 POSIX 实现中工作。但是请注意,Solaris/bin/sh 并不属于这一类。它的 test实现不支持 -e标志。
# 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
}

我不喜欢公布的 ls - A解决方案。最有可能的情况是,您希望测试该目录是否为空,因为您不希望删除它。下面就是这样。然而,如果您只是希望记录一个空文件,当然删除和重新创建它比列出可能无限的文件更快?

这个应该可以。

if !  rmdir ${target}
then
echo "not empty"
else
echo "empty"
mkdir ${target}
fi

测试特定的目标目录

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;

三个绝招


shopt -s nullglob dotglob; f=your/dir/*; ((${#f}))

这个技巧是100% bash并调用(衍生)一个子 shell。

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 频道的常见问题还有一个类似的选择和更多的细节(以及更多的例子) :

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)" ]

这个技巧的灵感来自于2007年发布的 NixCraft 的文章。添加 2>/dev/null来抑制输出错误 "No such file or directory"
另见 安德鲁 · 泰勒的答案(2008)和 Gr8can8dian的答案(2011)。

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

或者一句话的抨击版本:

[[ $(ls -A your/dir) ]] && echo "contains files" || echo "empty"

注意: 当目录不存在时,ls返回 $?=2,但是文件和空目录之间没有区别。


[ -n "$(find your/dir -prune -empty)" ]

最后一个技巧的灵感来自 Gravstar 的答案,其中 -maxdepth 0-prune取代,并通过 Phil的评论得到改进。

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的变体:

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 -prunefind -maxdepth 0使用更少的字符
  • find -empty打印空目录和文件
  • find -type d只打印目录

注意: 你也可以用下面的简化版本来替换 [ -n "$(find your/dir -prune -empty)" ]:

if [ `find your/dir -prune -empty 2>/dev/null` ]
then
echo "empty (directory or file)"
else
echo "contains files (or does not exist)"
fi

最后这段代码可以在大多数情况下工作,但是要注意,恶意路径可以表达命令..。

我很惊讶 空目录伍勒奇指南没有被提及。这个指南,和所有的伍德真的,是一个必须阅读的 shell 类型的问题。

值得注意的是:

永远不要尝试解析 ls 输出。即使 ls-A 解决方案也可能中断(例如,在 HP-UX 上,如果您是 root 用户,ls-A 也会中断 完全相反,如果你不是 root... 不,我不能编造一些 非常愚蠢)。

事实上,人们可能希望完全避免直接的问题。通常人们想知道 目录是空的,因为他们想要做一些涉及其中的文件的事情,等等 例如,其中一个基于 find 的示例可能是一个合适的解决方案:

   # 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

最常见的,所有真正需要的是这样的东西:

   # Bourne
for f in ./*.mpg; do
test -f "$f" || continue
mympgviewer "$f"
done

换句话说,提出问题的人可能认为显式的空目录测试是 需要避免错误消息,如 mympgview: ./* . mpg: 没有这样的文件或目录,而实际上没有 这项测试是必须的。

到目前为止,我还没有看到一个使用 grep 的答案,我认为它会给出一个更简单的答案(没有太多奇怪的符号!).我会这么做 使用 bourne shell 检查目录中是否存在任何文件:

这将返回一个目录中的文件数:

ls -l <directory> | egrep -c "^-"

您可以在写入目录的位置填写目录路径。管道的前半部分确保每个文件的输出的第一个字符是“-”。然后 egrep 计算以此开始的行数 使用正则表达式的符号。现在你要做的就是存储你得到的数字,然后用下面的反引号比较:

 #!/bin/sh
fileNum=`ls -l <directory> | egrep -c "^-"`
if [ $fileNum == x ]
then
#do what you want to do
fi

X 是你选择的变量。

dir_is_empty() {
[ "${1##*/}" = "*" ]
}


if dir_is_empty /some/dir/* ; then
echo "huzzah"
fi

假设您在 /any/dir/you/check中没有一个名为 *的文件,它应该可以在 bash dash posh busybox shzsh上工作,但是(对于 zsh)需要 unsetopt nomatch

性能应该可以与任何使用 *(globb)的 ls相媲美,我猜对于有很多节点的目录来说速度会很慢(我的 /usr/bin有3000多个文件,速度没有那么慢) ,至少会使用足够的内存来分配所有的 dis/filename (以及更多) ,因为它们都被作为参数传递(解析)给函数,一些 shell 可能对参数的数量和/或参数的长度有限制。

一个可移植的快速 O (1)零资源方法来检查一个目录是否为空将是不错的。

更新

上面的版本不考虑隐藏的文件/目录,如果需要更多的测试,比如 Rich 的 sh (POSIX shell)技巧中的 is_empty:

is_empty () (
cd "$1"
set -- .[!.]* ; test -f "$1" && return 1
set -- ..?* ; test -f "$1" && return 1
set -- * ; test -f "$1" && return 1
return 0 )

但是,相反,我在想这样的事情:

dir_is_empty() {
[ "$(find "$1" -name "?*" | dd bs=$((${#1}+3)) count=1 2>/dev/null)" = "$1" ]
}

有人担心当 dir 为空时,与参数和 find 输出的尾部斜杠不同,并且尾部换行符(但这应该很容易处理) ,遗憾的是,在我的 busybox sh上显示了 find -> dd管道上可能存在的 bug,输出随机截断(如果我使用 cat,输出总是相同的,似乎是 dd和参数 count)。

对我来说很有效(当你存在的时候) :

some_dir="/some/dir with whitespace & other characters/"
if find "`echo "$some_dir"`" -maxdepth 0 -empty | read v; then echo "Empty dir"; fi

全面检查:

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

这将告诉我目录是否为空,以及它包含的文件数量。

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

布鲁诺的回答的小变化:

files=$(ls -1 /some/dir| wc -l)
if [ $files -gt 0 ]
then
echo "Contains files"
else
echo "Empty"
fi

对我有用

把李子和最后的答案混在一起,我必须这么做

find "$some_dir" -prune -empty -type d | read && echo empty || echo "not empty"

也适用于有空格的路径

对于 Bash的简单回答是:

if [[ $(ls /some/dir/) ]]; then echo "huzzah"; fi;

我会选 find:

if [ -z "$(find $dir -maxdepth 1 -type f)" ]; then
echo "$dir has NO files"
else
echo "$dir has files"

这将检查只在目录中查找文件的输出,而不会遍历子目录。然后它使用取自 man test-z选项检查输出:

   -z STRING
the length of STRING is zero

看看一些结果:

$ mkdir aaa
$ dir="aaa"

空白目录:

$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

里面有很多字:

$ mkdir aaa/bbb
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

目录中的文件:

$ touch aaa/myfile
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
$ rm aaa/myfile

子目录中的文件:

$ touch aaa/bbb/another_file
$ [ -z "$(find aaa/ -maxdepth 1 -type f)" ] && echo "empty"
empty

尝试使用命令 find。 指定硬编码的目录或作为参数。 然后启动 find 来搜索目录中的所有文件。 检查 find 的返回是否为 null。 回声找到的数据

#!/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 \n\n "
echo "$_FIND"
else
echo "empty (or does not exist)"
fi

ZSH

我知道这个问题被标记为 bash; 但是,仅供参考,对于 用户:

测试非空目录

检查 foo是否为非空:

$ for i in foo(NF) ; do ... ; done

其中,如果 foo非空,则将执行 for块中的代码。

测试空目录

检查 foo是否为空:

$ for i in foo(N/^F) ; do ... ; done

其中,如果 foo为空,将执行 for块中的代码。

笔记

我们不需要引用上面的目录 foo,但是如果需要,我们可以这样做:

$ for i in 'some directory!'(NF) ; do ... ; done

我们还可以测试多个对象,即使它不是一个目录:

$ 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

任何不是目录的内容都将被忽略。

临时演员

因为我们是 globbing,所以我们可以使用任何 globb (或者括号展开) :

$ 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

我们还可以检查放置在数组中的对象:

$ 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块中的代码显然是依次在每个目录上执行的。如果不需要,那么您可以简单地填充一个数组参数,然后对该参数进行操作:

$ for i in *(NF) ; do full_directories+=($i) ; done
$ do_something $full_directories

解释

对于 zsh 用户,有一个 (F) globb 限定符(参见 man zshexpn) ,它匹配“ full”(非空)目录:

$ 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)列出匹配的对象: 是一个目录 AND 不是空的。因此,(^F)匹配: 不是目录 OR 为空。因此,例如,仅 (^F)也会列出常规文件。因此,正如在 zshexp手册页中解释的那样,我们还需要 (/) globb 限定符,它只列出目录:

$ 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

因此,要检查给定的目录是否为空,可以运行:

$ mkdir X
$ for i in X(/^F) ; do echo $i ; done ; echo "finished"
X
finished

并且只是为了确保不会捕获一个非空目录:

$ 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)匹配的目录(“目录为空的”) ,因此会显示一条错误消息,表示没有找到与 globb 匹配的目录。因此,我们需要使用 (N) globb 限定符来抑制这些可能的错误消息:

$ mkdir Y
$ touch Y/.f
$ for i in Y(N/^F) ; do echo $i ; done ; echo "finished"
finished

因此,对于空目录,我们需要限定符 (N/^F),可以理解为: “不要警告我失败,目录没有满”。

类似地,对于非空目录,我们需要限定符 (NF),我们可以将其理解为: “不要警告我失败,完整目录”。

通过一些变通方法,我可以找到一种简单的方法来确定目录中是否存在文件。这可以通过 grep 命令进行更多的扩展,以便专门进行检查。Xml 或。文本文件等。例句: ls /some/dir | grep xml | wc -l | grep -w "0"

#!/bin/bash
if ([ $(ls /some/dir | wc -l  | grep -w "0") ])
then
echo 'No files'
else
echo 'Found files'
fi

从 olibre 的回答中得到一些提示,我喜欢 Bash 函数:

function isEmptyDir {
[ -d $1 -a -n "$( find $1 -prune -empty 2>/dev/null )" ]
}

因为虽然它创建了一个子 shell,但是它和我能想象的 O (1)解决方案非常接近,并且给它一个名称可以使它具有可读性。然后我就可以写作了

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) ,有一些异常情况: 如果一个大目录除了最后一个条目之外已经全部或全部删除,那么“ find”可能必须读取整个目录,以确定它是否为空。我相信预期的性能是 O (1) ,但是最坏的情况是目录大小是线性的。我还没量过。

在另一个线程 如何使用 find 测试目录是否为空中,我提出了这一点

[ "$(cd $dir;echo *)" = "*" ] && echo empty || echo non-empty

基于这样的理由,$dir 确实存在,因为问题是“从 shell 脚本检查目录是否包含文件”,即使在大 dir 上,* 也不是那么大,在我的系统/usr/bin/* 上只有12Kb。

更新: Thanx@hh skladby,固定版。

[ "$(cd $dir;echo .* *)" = ". .. *" ] && echo empty || echo non-empty
if [[ -s somedir ]]; then
echo "Files present"
fi

在我使用 bash 5.0.17进行的测试中,如果 somedir有任何子级,则 [[ -s somedir ]]将返回 true。[ -s somedir ]也是如此。注意,如果存在隐藏文件或子目录,这也将返回 true。它也可能是依赖于文件系统的。

如果没有 调用像 LS 这样的工具,查找等:

POSIX 安全,即不依赖于您的 Bash/xyz shell/ls/etc 版本:

dir="/some/dir"
[ "$(echo $dir/*)x" != "$dir/*x" ] || [ "$(echo $dir/.[^.]*)x" != "$dir/.[^.]*x" ] || echo "empty dir"

这个想法是:

  • echo *列出非点文件
  • echo .[^.]*列出了除“ .”和“ . .”之外的点文件
  • 如果 echo没有找到匹配,它返回搜索表达式,即这里的 *.[^.]*-它们都不是真正的字符串,必须连接在一起,例如一个字母来强制一个字符串
  • ||短路中交替使用各种可能性: 至少有一个非点文件或 dir 或至少有一个点文件或 dir 或目录在执行级别是空的: “如果第一个可能性失败,尝试下一个,如果这个失败,尝试下一个”; 这里从技术上讲,Bash“尝试执行”echo "empty dir",将您的操作放在空目录这里(例如 exit)。

使用符号链接进行了检查,但还没有检查更奇特的可能的文件类型。

感觉真的应该为一个空目录提供一个 test选项。 我将把编辑注释作为建议留给 test 命令的维护者,但是对应的文件存在于空文件中。

在这个将我带到这里的琐碎用例中,我并不担心通过大量文件的循环,也不担心。文件。我希望能找到上面提到的 test的“失踪”操作数。这就是战争。

在下面的示例中,目录空为空,而 full 具有文件。

$ for f in empty/*; do test -e $f; done
$ echo $?
1
$ for f in full/*; do test -e $f; done
$ echo $?
0

或者,更短更丑,但同样只适用于相对琐碎的用例:

$ echo empty/*| grep \*
$ echo $?
1


$ echo full/* | grep \*
$ echo $?
0