我的tldr答案是:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
它符合POSIX,并且并不重要,它通常比列出目录并将输出通过管道传输到grep的解决方案更快。
用法:
if emptydir adir
then
echo "nothing found"
else
echo "not empty"
fi
我喜欢答案https://unix.stackexchange.com/a/202276/160204,我将其重写为:
function emptydir {
! { ls -1qA "./$1/" | grep -q . ; }
}
它列出了目录并将结果通过管道传递到grep。相反,我提出了一个基于全局扩展和比较的简单函数。
function emptydir {
[ "$(shopt -s nullglob; echo "$1"/{,.[^.],..?}*)" = "" ]
}
此函数不是标准的POSIX,而是使用调用子shell $()
。我首先解释这个简单的函数,以便以后可以更好地理解最终的解决方案(请参见上面的tldr答案)。
说明:
如果不进行扩展,则左侧(LHS)为空,目录为空时就是这种情况。nullglob选项是必需的,因为否则,如果没有匹配项,则glob本身就是扩展的结果。(当目录为空时,使RHS与LHS的glob匹配将不起作用,因为当LHS glob匹配名为glob本身的单个文件时会出现误报:*
glob中的匹配*
文件名中的子字符串。 )大括号表达式{,.[^.],..?}
包含隐藏文件,但不包含 ..
或.
。
因为shopt -s nullglob
是在$()
(子外壳程序)内部执行的,所以它不会更改nullglob
当前外壳程序的选项,这通常是一件好事。另一方面,在脚本中设置此选项是一个好主意,因为在没有匹配项的情况下,全局变量很容易返回错误。因此,可以在脚本开始处设置nullglob选项,该函数将不需要它。让我们牢记这一点:我们想要一个与nullglob选项一起使用的解决方案。
注意事项:
如果我们没有目录访问权限,则该函数报告的内容与目录为空时相同。这也适用于列出目录并grep输出的函数。
该shopt -s nullglob
命令不是标准的POSIX。
它使用由创建的子外壳 $()
。没什么大不了的,但是如果我们能避免的话,那很好。
优点:
并不是说它真的很重要,但是根据进程中内核所花费的CPU时间量来衡量,此功能比上一个功能快四倍。
其他解决方案:
我们可以删除非POSIX shopt -s nullglob
的LHS命令,把串"$1/* $1/.[^.]* $1/..?*"
在RHS并分别消除所出现的误报的时候我们只有命名的文件'*'
,.[^.]*
或..?*
在目录:
function emptydir {
[ "$(echo "$1"/{,.[^.],..?}*)" = "$1/* $1/.[^.]* $1/..?*" ] &&
[ ! -e "$1/*" ] && [ ! -e "$1/.[^.]*" ] && [ ! -e "$1/..?*" ]
}
如果没有该shopt -s nullglob
命令,则删除子外壳现在很有意义,但是我们必须小心,因为我们希望避免单词拆分,但允许在LHS上进行全局扩展。尤其是避免单词拆分的引用是行不通的,因为它还可以防止全局扩展。我们的解决方案是单独考虑这些问题:
function emptydir {
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
我们仍然可以为各个glob进行单词拆分,但是现在可以了,因为只有在目录不为空时,它才会导致错误。我们添加了2> / dev / null,以在LHS上有许多与给定glob匹配的文件时丢弃错误消息。
我们回想起我们想要一种也可以与nullglob选项一起使用的解决方案。上面的解决方案使用nullglob选项失败,因为当目录为空时,LHS的也为空。幸运的是,它从未说过目录为空。它只能说它是空的。因此,我们可以单独管理nullglob选项。我们不能简单地添加案例[ "$1/"* = "" ]
等,因为这些案例将扩展为[ = "" ]
在语法上不正确的等。因此,我们改用 [ "$1/"* "" = "" ]
etc.。我们不得不再次考虑三种情况*
,..?*
以及.[^.]*
相匹配的隐藏文件,但不.
和..
。如果我们没有nullglob选项,则这些不会干扰,因为它们也永远不会说不为null时为空。因此,最终提出的解决方案是:
function emptydir {
[ "$1/"* "" = "" ] 2> /dev/null &&
[ "$1/"..?* "" = "" ] 2> /dev/null &&
[ "$1/".[^.]* "" = "" ] 2> /dev/null ||
[ "$1/"* = "$1/*" ] 2> /dev/null && [ ! -e "$1/*" ] &&
[ "$1/".[^.]* = "$1/.[^.]*" ] 2> /dev/null && [ ! -e "$1/.[^.]*" ] &&
[ "$1/"..?* = "$1/..?*" ] 2> /dev/null && [ ! -e "$1/..?*" ]
}
安全问题:
在一个空目录中创建两个文件,rm
然后x
在*
提示符下执行。该glob *
将扩展为rm x
,然后将其删除x
。这与安全性无关,因为在我们的函数中,全局位置位于扩展的位置,而不是将其视为命令,而是将其视为参数,就像in中一样for f in *
。