当前位置:首页> 正文

关于bash:从shell脚本检查目录是否包含文件

关于bash:从shell脚本检查目录是否包含文件

Checking from shell script if a directory contains files

从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 -prunefind -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 shzsh上运行,但是(对于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,则输出始终是相同的,似乎是带有参数countdd)。


我很惊讶没有提到空目录上的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

展开全文阅读

相关内容