Bash脚本:测试空目录


95

我想测试目录是否不包含任何文件。如果是这样,我将跳过一些处理。

我尝试了以下方法:

if [ ./* == "./*" ]; then
    echo "No new file"
    exit 1
fi

这给出了以下错误:

line 1: [: too many arguments

有解决方案/替代方案吗?


Answers:


120
if [ -z "$(ls -A /path/to/dir)" ]; then
   echo "Empty"
else
   echo "Not Empty"
fi

另外,检查目录是否以前也很酷。


11
不要使用&&,并||同时!如果echo "Not Empty"失败,echo "Empty"将运行!试试看echo "test" && false || echo "fail"!是的,我知道echo不会失败,但是如果您更改任何其他命令,您将会感到惊讶!
uzsolt 2011年

4
请提供至少一个上面的代码不起作用的示例。具体而言,此代码绝对正确。我希望
问问

3
[ "$(ls -A /)" ] && touch /non-empty || echo "Empty"-如果要“标记”具有创建的文件的非空目录non-empty,将失败并显示“空”。
uzsolt 2011年

4
这里是touch /empty我的线?
2011年

7
不好了!这段代码有一个非常重要的问题。应该是if [ -n "$(ls -A /path/to/dir)" ]; then...请先更新答案,然后再有人将此代码粘贴到他们的服务器中,然后黑客找出如何利用它。如果/path/to/dir不为空,那么那里的文件名将作为参数传递,/bin/test这显然是不希望的。只需添加-n参数,问题就解决了。谢谢!
爱德华·内德·哈维

21

无需计算任何东西或外壳球。您也可以read与结合使用find。如果find输出为空,则返回false

if find /some/dir -mindepth 1 | read; then
   echo "dir not empty"
else
   echo "dir empty"
fi

这应该是便携式的。


好的解决方案,但是我认为您的echo呼叫反映了错误的结果:在我的测试中(在Cygwin下)find . -mindepth 1 | read,非空目录中有141错误代码,空目录中有0错误代码
Lucas Cimon

@LucasCimon不在此处(macOS和GNU / Linux)。对于非空目录,read返回0,对于空目录,返回
1。– slhck

1
PSA无法使用set -o pipefail
三十二上校

19
if [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; then
    echo "Empty directory"
else
    echo "Not empty or NOT a directory"
fi

正确且快速。真好!
l0b0 2012年

4
它需要使用引号(2x)和-n正确且安全的测试(使用名称中带有空格的目录进行测试,使用名称“ 0 = 1”的非空目录进行测试)。... [ -n "$(find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty 2>/dev/null)" ]; ...
Zrin

1
@ivan_pozdeev事实并非如此,至少对于GNU查找而言。你可能会思考grepserverfault.com/questions/225798/...
弗拉基米尔Panteleev

编写起来可能更简单find "$DIR_TO_CHECK" -maxdepth 0 -type d -empty | grep .,并且依赖于grep的退出状态。无论采用哪种方式,这都是解决此问题的正确方法。
汤姆·安德森

13
#!/bin/bash
if [ -d /path/to/dir ]; then
    # the directory exists
    [ "$(ls -A /path/to/dir)" ] && echo "Not Empty" || echo "Empty"
else
    # You could check here if /path/to/dir is a file with [ -f /path/to/dir]
fi

4
一定是这样,不需要解析ls输出,只需查看它是否为空即可。使用find感觉对我来说太过分了。
akostadinov

4

这将在当前工作目录(。)中完成该工作:

[ `ls -1A . | wc -l` -eq 0 ] && echo "Current dir is empty." || echo "Current dir has files (or hidden files) in it."

或同一条命令分成三行只是为了提高可读性:

[ `ls -1A . | wc -l` -eq 0 ] && \
echo "Current dir is empty." || \
echo "Current dir has files (or hidden files) in it."

如果需要在其他目标文件夹上运行它,只需替换 ls -1A . | wc -lls -1A <target-directory> | wc -l

编辑:我替换-1a-1A(请参阅@Daniel评论)


2
使用ls -A代替。有些文件系统没有...根据文档的符号链接。
丹尼尔·贝克

1
谢谢@Daniel,我根据您的建议编辑了答案。我知道“ 1”也可能会被删除。
ztank1013 2011年

2
这没有什么坏处,但是如果不将其输出到终端,则暗示它。由于将其通过管道传输到另一个程序,因此是多余的。
丹尼尔·贝克

-1绝对是多余的。即使ls在管道传输时每行不打印一项,它也不会影响检查是否产生零行或更多行的想法。
维克多·亚雷玛

3

使用以下内容:

count="$( find /path -mindepth 1 -maxdepth 1 | wc -l )"
if [ $count -eq 0 ] ; then
   echo "No new file"
   exit 1
fi

这样,您就不会依赖的输出格式ls-mindepth跳过目录本身,-maxdepth防止递归防御子目录以加快处理速度。


当然,您现在依赖于wc -lfind输出格式(虽然相当简单)。
丹尼尔·贝克

3

使用数组:

files=( * .* )
if (( ${#files[@]} == 2 )); then
    # contents of files array is (. ..)
    echo dir is empty
fi

3
非常好的解决方案,但请注意,它需要shopt -s nullglob
xebeche

3
${#files[@]} == 2假设并不代表根目录(您可能不会测试它是否为空,但是有些代码可能不知道该限制)。
ivan_pozdeev '18年

3

hacky,但仅限bash,无PID的方式:

is_empty() {
    test -e "$1/"* 2>/dev/null
    case $? in
        1)   return 0 ;;
        *)   return 1 ;;
    esac
}

这利用了以下事实:test如果在后面加上多个参数,则内置函数将以2退出-e:首先,"$1"/*glob由bash扩展。这导致每个文件一个参数。所以

  • 如果没有文件,则星号test -e "$1"*不会展开,因此Shell将退回到尝试的名为的文件*,该文件返回1。

  • ...除非实际上有一个文件精确命名为*,否则星号会扩展为asterisk,其结果与上述相同,即。test -e "dir/*",这一次返回0。(感谢@TrueY指出这一点。)

  • 如果有一个文件,test -e "dir/file"则运行,返回0。

  • 但是,如果文件多于1,test -e "dir/file1" "dir/file2" 则运行该文件,bash将其报告为使用错误,即2。

case 环绕整个逻辑,以便仅将第一种情况(退出状态为1)报告为成功。

我尚未检查的可能问题:

  • 文件数量超过了允许的参数数量-我猜这可能与2个以上文件的情况类似。

  • 还是实际上有一个文件名是空的-我不确定在任何理智的OS / FS上都可以使用该文件。


1
较小的更正:如果dir /中没有文件,则将test -e dir/*其调用。如果目录中唯一的文件是“ *”,那么test将返回0。如果有更多文件,则返回2。
TrueY

您是对的,@ TrueY,我已将其合并到答案中。谢谢!
Alois Mahdal

2

使用FIND(1)(在Linux和FreeBSD下),您可以通过“ -maxdepth 0”以非递归方式查看目录条目,并使用“ -empty”测试其是否为空。应用于以下问题:

if test -n "$(find ./ -maxdepth 0 -empty)" ; then
    echo "No new file"
    exit 1
fi

它可能不是100%可移植的,但是很优雅。
Craig Ringer

1

我认为最好的解决方案是:

files=$(shopt -s nullglob; shopt -s dotglob; echo /MYPATH/*)
[[ "$files" ]] || echo "dir empty" 

感谢https://stackoverflow.com/a/91558/520567

这是对我的答案的匿名编辑,可能对某人无济于事:稍作改动就会得出文件的数量:

files=$(shopt -s nullglob dotglob; s=(MYPATH/*); echo ${s[*]}) 
echo "MYPATH contains $files files"

即使文件名包含空格,这也将正常工作。


1
if find "${DIR}" -prune ! -empty -exit 1; then
    echo Empty
else
    echo Not Empty
fi

编辑:在快速查看实现之后,我认为该解决方案可以与gnu find一起正常工作。但这可能不适用于例如netbsd的find。确实,那是使用stat(2)的st_size字段。该手册将其描述为:

st_size            The size of the file in bytes.  The meaning of the size
                   reported for a directory is file system dependent.
                   Some file systems (e.g. FFS) return the total size used
                   for the directory metadata, possibly including free
                   slots; others (notably ZFS) return the number of
                   entries in the directory.  Some may also return other
                   things or always report zero.

更好,更简单的解决方案是:

if find "${DIR}" -mindepth 1 -exit 1; then
    echo Empty
else
    echo Not Empty
fi

同样,第一种解决方案中的-prune无效。

编辑:gnu查找没有-exit。对于GNU查找,这应该起作用:

if [ -z "`find \"${DIR}\" -mindepth 1 -exec echo notempty \; -quit`" ]; then
    echo Empty
else
    echo Not Empty
fi

GNU findutils 4.6.0(最新版本)查找没有-exit谓词
丹尼斯

0

这都是很棒的东西-只需将其放入脚本中,这样我就可以检查当前目录下的空目录。将以下内容放入名为“ findempty”的文件中,放置在路径中的某个位置,以便bash可以找到它,然后运行chmod 755。我猜可以很容易地修改为您的特定需求。

#!/bin/bash
if [ "$#" == "0" ]; then 
find . -maxdepth 1 -type d -exec findempty "{}"  \;
exit
fi

COUNT=`ls -1A "$*" | wc -l`
if [ "$COUNT" == "0" ]; then 
echo "$* : $COUNT"
fi

0

这项工作对我来说有用,以检查和处理目录中的文件../IN,考虑脚本在../Script目录中:

FileTotalCount=0

    for file in ../IN/*; do
    FileTotalCount=`expr $FileTotalCount + 1`
done

if test "$file" = "../IN/*"
then

    echo "EXITING: NO files available for processing in ../IN directory. "
    exit

else

  echo "Starting Process: Found ""$FileTotalCount"" files in ../IN directory for processing."

# Rest of the Code

0

如何测试目录是否存在并且在if语句中不为空

if [[ -d path/to/dir && -n "$(ls -A path/to/dir)" ]]; then 
  echo "directory exists"
else
  echo "directory doesn't exist"
fi

-1

对于当前目录以外的任何目录,您都可以尝试尝试检查其是否为空rmdir,因为rmdir对于非空目录,它肯定会失败。如果rmdir成功,并且您实际上希望空目录在测试中幸存下来,请mkdir再次执行。

如果他们知道短暂停止存在的目录可能会破坏其他进程,请不要使用此技巧。

如果rmdir对您不起作用,并且您可能正在测试可能包含大量文件的目录,则任何依赖Shell Globing的解决方案都可能变慢和/或遇到命令行长度限制。find在这种情况下可能会更好。find我能想到的最快的解决方案是

is_empty() {
    test -z $(find "$1" -mindepth 1 -printf X -quit)
}

它适用于GNU和BSD版本find的Solaris ,但不适用于Solaris 版本,因为Solaris缺少这些find运算符中的每一个。热爱您的工作,Oracle。


这不是一个好主意。OP只是想测试目录是否为空。
roaima

-3

您可以尝试删除目录并等待错误; 如果目录不为空,则rmdir不会删除该目录。

_path="some/path"
if rmdir $_path >/dev/null 2>&1; then
   mkdir $_path        # create it again
   echo "Empty"
else
   echo "Not empty or doesn't exist"
fi

3
-1这是适得其反的代码。rmdir如果我无权删除目录,将失败;或者它是Btrfs子卷;或者它属于只读文件系统。如果rmdir没有失败并且mkdir可以运行:如果已经删除的目录属于另一个用户怎么办?它的(可能是非标准的)权限如何?ACL?扩展属性?全部丢失。
卡米尔Maciorowski

好吧,我只是在学习bash,我认为这可能是一种更快的方法,而不是遍历所有目录,但是CPU功能强大,而且您是正确的而不是安全的。
impxd 18'Apr
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.