我试图检查一个文件是否存在,但与通配符。以下是我的例子:
if [ -f "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
我也试过不加双引号。
我试图检查一个文件是否存在,但与通配符。以下是我的例子:
if [ -f "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
我也试过不加双引号。
解决方案:
files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
echo "Exists"
else
echo "None found."
fi
> Exists
试试这个
fileTarget="xorg-x11-fonts*"
filesFound=$(ls $fileTarget)
case ${filesFound} in
"" ) printf "NO files found for target=${fileTarget}\n" ;;
* ) printf "FileTarget Files found=${filesFound}\n" ;;
esac
Test
fileTarget="*.html" # Where I have some HTML documents in the current directory
FileTarget Files found=Baby21.html
baby22.html
charlie 22.html
charlie21.html
charlie22.html
charlie23.html
fileTarget="xorg-x11-fonts*"
NO files found for target=xorg-x11-fonts*
注意,这只适用于当前目录,或者变量fileTarget包含您想要检查的路径的地方。
对于Bash脚本,最直接和最有效的方法是:
if compgen -G "${PROJECT_DIR}/*.png" > /dev/null; then
echo "pattern exists!"
fi
即使在有数百万个文件的目录中,这也会非常快地工作,而且不涉及新的子shell。
源
最简单的应该是依赖ls返回值(当文件不存在时,它返回非零):
if ls /path/to/your/files* 1> /dev/null 2>&1; then
echo "files do exist"
else
echo "files do not exist"
fi
我重定向ls输出,使其完全静音。
下面是一个同样依赖于glob展开的优化,但避免使用ls:
for f in /path/to/your/files*; do
## Check if the glob gets expanded to existing files.
## If not, f here will be exactly the pattern above
## and the exists test will evaluate to false.
[ -e "$f" ] && echo "files do exist" || echo "files do not exist"
## This is all we needed to know, so we can break after the first iteration
break
done
这与grok12的答案非常相似,但它避免了在整个列表中进行不必要的迭代。
for i in xorg-x11-fonts*; do
if [ -f "$i" ]; then printf "BLAH"; fi
done
这将适用于多个文件,文件名中有空白。
如果您的shell有一个nullglob选项,并且该选项被打开,则不匹配任何文件的通配符模式将从命令行中完全删除。这将使ls看不到任何pathname参数,列出当前目录的内容并成功,这是错误的。如果没有参数或参数命名了一个不存在的文件,GNU stat总是会失败,它将更加健壮。另外,&>重定向操作符是bashism。
if stat --printf='' /path/to/your/files* 2>/dev/null
then
echo found
else
echo not found
fi
更好的是GNU find,它可以在内部处理通配符搜索,并在找到一个匹配文件时立即退出,而不是浪费时间处理由shell扩展的潜在的巨大列表;这也避免了shell可能溢出其命令行缓冲区的风险。
if test -n "$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)"
then
echo found
else
echo not found
fi
非gnu版本的find可能没有此处使用的-maxdepth选项,以使find只搜索/dir/to/search,而不是整个目录树。
Use:
if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
我用这个:
filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`
if [ $filescount -gt 0 ]; then
blah
fi
PowerShell对通配符的处理方式是不同的,你可以像下面这样把它放在引号里:
If (Test-Path "./output/test-pdf-docx/Text-Book-Part-I*"){
Remove-Item -force -v -path ./output/test-pdf-docx/*.pdf
Remove-Item -force -v -path ./output/test-pdf-docx/*.docx
}
我认为这是有帮助的,因为最初问题的概念涵盖了一般的“shell”,而不仅仅是Bash或Linux,也适用于有相同问题的PowerShell用户。
恕我直言,最好在测试文件、glob或目录时使用find always。这样做的绊脚石是find的退出状态:如果成功遍历所有路径,则为0,否则为>。传递给find的表达式在退出代码中没有创建回显。
下面的示例测试目录是否有条目:
$ mkdir A
$ touch A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty'
not empty
当A没有文件时,grep失败:
$ rm A/b
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty'
empty
当A不存在时,grep再次失败,因为查找只打印到stderr:
$ rmdir A
$ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' || echo 'empty'
find: 'A': No such file or directory
empty
用其他find表达式替换-not -empty,但如果-exec命令打印到stdout,请小心。在这种情况下,您可能需要使用grep来获得更具体的表达式。
这种方法在shell脚本中工作得很好。最初的问题是寻找glob xorg-x11-fonts*:
if find -maxdepth 0 -name 'xorg-x11-fonts*' -print | head -n1 | grep -q .
then
: the glob matched
else
: ...not
fi
注意,如果xorg-x11-fonts*没有匹配,或者find遇到错误,就会到达else-branched。使用$?来区分大小写。
我使用的Bash代码:
if ls /syslog/*.log > /dev/null 2>&1; then
echo "Log files are present in /syslog/;
fi
严格来说,如果你只想打印“Blah”,下面是解决方案:
find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit
这里有另一种方法:
doesFirstFileExist(){
test -e "$1"
}
if doesFirstFileExist xorg-x11-fonts*
then printf "BLAH"
fi
但我认为最优的是如下所示,因为它不会尝试对文件名进行排序:
if [ -z $(find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit) ]
then
printf "BLAH"
fi
下面是针对您的特定问题的解决方案,它不需要for循环或ls、find等外部命令。
if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then
printf "BLAH"
fi
正如您所看到的,它只是比您希望的稍微复杂一些,并且依赖于这样一个事实,即如果shell不能展开glob,这意味着不存在具有该glob的文件,echo将按原样输出glob,这允许我们仅做字符串比较来检查是否存在这些文件。
但是,如果我们要概括这个过程,我们应该考虑到这样一个事实,即文件的名称和/或路径中可能包含空格,并且glob char可以合理地扩展为零(在您的示例中,这将是文件名正好为xorg-x11-fonts的文件的情况)。
这可以通过bash中的以下函数来实现。
function doesAnyFileExist {
local arg="$*"
local files=($arg)
[ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e "${files[0]}" ]
}
回到您的示例,可以像这样调用它。
if doesAnyFileExist "xorg-x11-fonts*"; then
printf "BLAH"
fi
Glob展开应该发生在函数内部,以便它正常工作,这就是为什么我把参数放在引号中,这就是函数体中的第一行:因此任何多个参数(可能是函数外部Glob展开的结果,以及一个虚假的形参)将合并为一个。另一种方法是,如果有多个实参,就引发错误,还有一种方法是忽略除第一个实参以外的所有实参。
函数体中的第二行将files var设置为由glob扩展到的所有文件名组成的数组,每个数组元素对应一个文件名。如果文件名包含空格是可以的,每个数组元素将按原样包含文件名,包括空格。
函数体中的第三行做了两件事:
It first checks whether there's more than one element in the array. If so, it means the glob surely got expanded to something (due to what we did on the 1st line), which in turn implies that at least one file matching the glob exist, which is all we wanted to know. If at step 1. we discovered that we got less than 2 elements in the array, then we check whether we got one and if so we check whether that one exist, the usual way. We need to do this extra check in order to account for function arguments without glob chars, in which case the array contains only one, unexpanded, element.
您可以执行以下操作:
set -- xorg-x11-fonts*
if [ -f "$1" ]; then
printf "BLAH"
fi
这适用于sh和衍生品:KornShell和Bash。它不会创建任何子壳。$(..)和'…在其他解决方案中使用的命令会创建一个子shell:它们会派生一个进程,而且效率很低。当然,它可以处理多个文件,而且这个解决方案可能是最快的,或者仅次于最快的。
当没有任何匹配时,它也能工作。没有必要像某位评论员所说的那样使用nullglob。$1将包含原始的测试名称,因此test -f $1将不会成功,因为$1文件不存在。
Use:
if ls -l | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob
then
# do something
else
# do something else
fi
如果网络文件夹中有大量文件,使用通配符是有问题的(速度,或命令行参数溢出)。
最后我得到了:
if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then
echo Such file exists
fi
if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi
在KornShell, Bash和Z shell shell中使用新的花哨的shmancy特性(这个例子不处理文件名中的空格):
# Declare a regular array (-A will declare an associative array. Kewl!)
declare -a myarray=( /mydir/tmp*.txt )
array_length=${#myarray[@]}
# Not found if the first element of the array is the unexpanded string
# (ie, if it contains a "*")
if [[ ${myarray[0]} =~ [*] ]] ; then
echo "No files not found"
elif [ $array_length -eq 1 ] ; then
echo "File was found"
else
echo "Files were found"
fi
for myfile in ${myarray[@]}
do
echo "$myfile"
done
是的,这闻起来确实像Perl。我很高兴我没有踩进去;)
我发现了一些值得分享的巧妙解决方案。第一个仍然存在“如果匹配太多就会破坏”的问题:
pat="yourpattern*" matches=($pat) ; [[ "$matches" != "$pat" ]] && echo "found"
(回想一下,如果你使用一个没有[]语法的数组,你会得到数组的第一个元素。)
如果你在你的脚本中有"shop -s nullglob",你可以简单地这样做:
matches=(yourpattern*) ; [[ "$matches" ]] && echo "found"
现在,如果一个目录中可能有大量的文件,你很可能会使用find:
find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'