关于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

推荐阅读

    更新文件命令linux?

    更新文件命令linux?,工作,系统,地址,信息,时间,命令,目录,基础,标准,网络,lin

    linux下读取文件命令?

    linux下读取文件命令?,系统,工作,地址,数字,图片,信息,网络,命令,文件,一致,l

    linux改文件夹名命令?

    linux改文件夹名命令?,名字,软件,命令,文件,系统,目录,目标,文件名,源文件,

    linux建文件命令格式?

    linux建文件命令格式?,系统,名字,名称,时间,密码,命令,文件,文件夹,不了,数

    下载文件的命令linux?

    下载文件的命令linux?,平台,工具,服务,密码,软件,网络,位置,代理,手机,工作,

    linux命令文件传输?

    linux命令文件传输?,系统,数据,命令,文件,基本知识,源文件,目录,目标,功能,

    linux文件全选命令?

    linux文件全选命令?,电脑,系统,环境,代码,平台,服务,快捷键,文件,命令,权限,l

    linux浏览文件类命令?

    linux浏览文件类命令?,系统,信息,数据,情况,命令,标准,时间,文件,概念,管理,

    linux打包文件夹命令?

    linux打包文件夹命令?,系统,工具,管理,图片,文件,命令,位置,软件,目录,格式,l

    linux中统计文件命令?

    linux中统计文件命令?,系统,信息,数据,情况,工作,文件,时间,档案,标准,名称,L

    linux重名名文件命令?

    linux重名名文件命令?,图片,名称,名字,文件,命令,位置,代码,软件,系统,文件

    linux的建立文件命令?

    linux的建立文件命令?,名称,系统,时间,名字,命令,文件夹,位置,密码,不了,文

    linux06文件命令?

    linux06文件命令?,数字,系统,工作,第一,名字,信息,管理,文件,目录,命令,linux

    linux将文件备份命令?

    linux将文件备份命令?,系统,密码,设备,软件,通讯,较大,认证,服务,数据,文件,L

    linux运行脚本命令?

    linux运行脚本命令?,系统,代码,服务,文件,工具,平台,网站,脚本,命令,方法,Lin

    linux扩展文件命令?

    linux扩展文件命令?,工作,地址,系统,信息,命令,目录,管理,情况,文件,标准,Lin

    linux命令新增文件夹?

    linux命令新增文件夹?,系统,名字,首次,名称,新增,文件,命令,文件夹,地址,密

    linux推送文件命令?

    linux推送文件命令?,地址,系统,情况,工作,命令,文件,电脑,密码,信息,目录,Lin

    linux文件替换命令行?

    linux文件替换命令行?,资料,命令,文件,数据,名称,系统,实时,工作,字符串,批