sh linux脚本文件是否存在,关于sh:检查shell脚本中是否存在带通配符的文件

本问题已经有最佳答案,请猛点这里访问。

我正在尝试检查文件是否存在,但使用通配符。下面是我的例子:

if [ -f"xorg-x11-fonts*" ]; then

printf"BLAH"

fi

我也试过没有双引号。

您的代码有两个错误:(1)星号必须在双引号之外(带引号的星号失去了它特殊的通配符含义),以及(2)如果多个文件与模式匹配,多个参数将传递给[命令,很可能导致[退出并出现错误,因此被解释为没有文件匹配。

最简单的应该是依赖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输出,使其完全静音。

编辑:由于这个答案引起了人们的注意(以及非常有用的评论评论),这里有一个优化,它也依赖于全局扩展,但避免了使用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的答案非常相似,但它避免了整个列表中不必要的迭代。

警告:在Debian Almquist shell(dash)中,安装在Debian和Ubuntu-&>中的/bin/sh上,似乎丢弃了退出代码,从而破坏了此解决方案。一个解决办法是改为使用> /dev/null 2>&1进行重定向。

在有许多文件的目录中(可能是由于排序),ls的速度可能相当慢。您可能希望至少使用-U关闭排序。

如果你要处理大量的搜索,把这些文件列成文本文件并查看它会更好吗?

@Musiphil感谢您的输入。我编辑了答案,添加了一个优化的替代方案,消除了使用ls。

@Costiciudatu您检查过当目录名中有空格时,这个选项是如何工作的吗?例如,for f in /path/to/your files*不是被解释为两个论点,/path/to/your和files*吗?我试过用双引号括起来,但没用(即使有文件也找不到)。

@伊兹,你应该把这句话用双引号引起来,但把*放在外面:for f in"/path/to/your files"*应该管用。

@科斯蒂丘杜图确认,谢谢!你可能希望把它和你的答案结合起来,以便更容易找到:)我同时同意了潘卡吉的答案——但很高兴知道另一个答案:)

警告。如果目录包含大量文件,这将失败。"*"的扩展将超过命令行长度限制。

@但是,如果文件本身在其名称中有空格,它仍然会失败,不是吗?

我发布了一个不需要任何for循环,也不需要任何外部工具的解决方案,并且使用包含空格的文件名:stackoverflow.com/a/40808284/566849

单侧注释…并非所有的Linux/Unix设备都支持glob。例如,我工作的一个Unix服务器不支持glob。

如果您的shell有一个nullglob选项,并且它已经打开,那么一个不匹配任何文件的通配符模式将从命令行中完全删除。这将使ls看不到路径名参数,列出当前目录的内容并成功,这是错误的。gnu stat,如果没有参数或者参数命名一个不存在的文件,它总是会失败,这将更加健壮。此外,&>重定向运算符是一种bashim。

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 search仅为/dir/to/search,而不是根目录树的整个目录树。

让find处理通配符是最好的,因为bash在扩展模式时,试图对匹配文件名的列表进行排序,这是浪费的,而且成本高昂。

@如果目录中只有几个文件(或没有),那么启动外部进程(如find)的musiphil会更加浪费。

@多尔曼:你说得对。我想这完全取决于情况;另一方面,如果有大量的文件,那么bash的通配符扩展可能比启动find花费更多的时间。

@更糟的是,如果有大量的通配符扩展,只会失败。但在这种情况下,扩大find的巨大产出也可能失败。还要注意,列出所有带有find just to then测试输出是否为空的文件是对资源的极大浪费。

如果找不到文件,find命令将创建一条难看的错误消息:find: '/dir/to/search': No such file or directory;您可以使用-quit 1> /dev/null 2>&1抑制此错误。

@Dolmen:运行find和-quit,如@flabdablet's post中所述,不会受到大量文件的影响,因为它一旦找到第一个匹配就退出,因此不会列出所有文件。所以这并不像你建议的那样浪费资源。此外,find并不像shell那样简单地"扩展"通配符,而是根据模式检查它找到的每个文件,看它是否匹配,因此对于大量文件来说,它不会失败。

这是我的答案-

files=(xorg-x11-fonts*)

if [ -e"${files[0]}" ];

then

printf"BLAH"

fi

如果zsh报告错误,您应该添加unsetopt nomatch。

和shopt -s nullglob代表bash。

也许应该更清楚地指出,使用一个数组可以使它明显地非posix sh。

for i in xorg-x11-fonts*; do

if [ -f"$i" ]; then printf"BLAH"; fi

done

这将适用于多个文件和文件名中的空白。

如果有多个匹配项,它将打印多个"blah"。可能在第一场比赛后添加一个break退出循环。

这个(有@triplee的休息)得到了我的选票。通过只使用本机globbing和文件测试操作符,它甚至避免了使用ls、find或forwarding globs等命令时出现的角情况问题。我认为它没有为其他答案而提出的所有问题,比如带空格的名称、nullglob设置和bashims。我做了一个函数:existsAnyFile () { for file; do [ -f"$file" ] && return 0; done; false; }。

注意,如果XORG-X11-FONTS*不存在,这将导致stat失败,这可能不是您想要的。

您可以执行以下操作:

set -- xorg-x11-fonts*

if [ -f"$1" ]; then

printf"BLAH"

fi

这适用于sh和派生词:ksh和bash。它不会创建任何子shell。$(…)和`…`命令创建一个子shell:它们分叉一个进程,而且效率低下。当然,它可以处理多个文件,而且这个解决方案可以是最快的,也可以是第二快的。

在没有火柴的情况下也能用。正如一位演讲人所说,不需要使用nullglob。$1将包含原始测试名称,因此测试-f$1不会成功,因为$1文件不存在。

最便携的解决方案!

唉,没有火柴就不行。$1将包含原始测试名称,包括*。您可以在bash中设置"nullglob",这样它将空白。不过,那不是便携式的:)

克里斯,如果没有匹配的话,$1将包含原始测试名称,包括*如您所说。然后测试:[-f"$1"]将无法成功完成,因为文件"*"不存在。因此,您不需要nullglob或其他技巧。它是100%便携式的。

更新:

好吧,现在我有了解决方案:

files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l)

if ["$files" !="0" ]

then

echo"Exists"

else

echo"None found."

fi

> Exists

在我的shell(zsh)中,如果只有一个与glob匹配,那么它就可以工作,否则它将扩展所有文件,测试将失败(参数太多)。

更新我的代码。我确信这是可行的,我刚安装了zsh并测试过。

重新更新。我的错。

在有许多文件的目录中(可能是由于排序),ls的速度可能相当慢。您可能希望至少使用-U关闭排序。

如果globbing匹配一个目录名,ls将吐出该目录的内容,这可能导致误报。

运行ls和wc需要启动两个外部程序。一点效率都没有。

也许这对某人有帮助:

if ["`echo xorg-x11-fonts*`" !="xorg-x11-fonts*" ]; then

printf"BLAH"

fi

这是一个最简单、最简单、最优雅的答案。

@Sergestroobant不确定我是否同意。在这里命令替换可能是必要的,但它会刺激我的畏缩反射。

赞成。。。比如,如果文件的字面名称为xorg-x11-fonts\*,会怎么样?

一点也不优雅,因为它分叉一个子shell来运行echo命令。

不优雅,丑陋的AF

这个问题并不是针对Linux/bash的,所以我想我应该添加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用户。

严格来说,如果你只想打印"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

您也可以使用-exec选项,这样查找:find . -maxdepth 1 -name '*.o' -exec rm {} \;

我使用的bash代码

if ls /syslog/*.log > /dev/null 2>&1; then

echo"Log files are present in /syslog/;

fi

谢谢!

这里有一个针对您的特定问题的解决方案,它不需要for循环或外部命令,如ls、find等。

if ["$(echo xorg-x11-fonts*)" !="xorg-x11-fonts*" ]; then

printf"BLAH"

fi

正如你所看到的,这只是比你所期望的要复杂一点,它依赖于这样一个事实:如果shell不能扩展glob,它就意味着不存在具有该glob的文件,并且echo将按原样输出glob,这允许我们做一个简单的字符串比较来检查这些文件是否存在。

但是,如果我们要推广该过程,我们应该考虑到这样一个事实:文件的名称和/或路径中可能包含空格,并且glob char可以正确地扩展为Nothing(在您的示例中,该文件的名称正好是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扩展的结果,也可能是伪参数)都可以合并为一个参数。另一种方法可能是,如果有多个参数,则会引发错误;另一种方法可能是忽略除第一个参数以外的所有参数。

函数体中的第二行将filesvar设置为一个数组,该数组由全局扩展到的所有文件名组成,每个数组元素一个。如果文件名包含空格,那么每个数组元素都将包含原样的名称,包括空格。

函数体中的第三行有两个功能:

它首先检查数组中是否有多个元素。如果是这样的话,那就意味着地球肯定扩展到了某个地方(由于我们在第一行所做的),这反过来意味着至少存在一个与地球匹配的文件,这就是我们想要知道的。

如果在步骤1。我们发现数组中的元素少于2个,然后我们检查是否有一个元素,如果有,我们检查这个元素是否存在,通常的方法。我们需要做这个额外的检查,以说明没有glob字符的函数参数,在这种情况下,数组只包含一个未展开的元素。

这是低效的,因为$(..)启动了一个子shell。

@像其他任何一个过程一样,Dolmen A Sub Shell也只是一个过程。接受的答案启动了ls命令,无论出于何种目的,它都和子shell一样高效(或低效)。

我从来没有写过接受的答案更好,如果我是提交者,我会接受的。

注意,我在这个非常相同的答案中解释的广义方法根本不使用任何子shell。

我用这个:

filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'`

if [ $filescount -gt 0 ]; then

blah

fi

对于这个任务,wc -l比awk更有效。

计算结果的数量无论如何都是一个反模式。通常,您只想查看ls是否返回成功(或者更好地避免ls,并使用shell的内置功能)。

if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi

imho最好在测试文件、全局或目录时始终使用find。这样做的绊脚石是find的退出状态:如果所有路径都成功遍历,则为0;否则为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不存在时,由于find只打印到stderr,grep再次失败:

$ 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脚本中很好地工作。最初的问题是寻找环球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遇到错误,则到达另一个分支。为了区分案件,使用$?。

当使用-name时,可能意味着-maxdepth 1,因为-maxdepth 0将查看当前目录而不是其内容。

找到了几个值得分享的解决方案。第一个问题仍然是"如果匹配太多,这将中断"问题:

pat="yourpattern*" matches=($pat) ; [["$matches" !="$pat" ]] && echo"found"

(回想一下,如果使用不带[ ]语法的数组,则会得到数组的第一个元素。)

如果脚本中有"shopt-s nullglob",您只需执行以下操作:

matches=(yourpattern*) ; [["$matches" ]] && echo"found"

现在,如果一个目录中可能有大量的文件,那么使用find非常困难:

find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'

怎么样

if ls -l  | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob

then

# do something

else

# do something else

fi

不,不要在脚本中使用ls,.*通配符是多余的(您可能是指grep -q '^xorg-x1-fonts')。

为什么不分析ls。

如果使用通配符的网络文件夹上有大量文件,则存在问题(速度或命令行参数溢出)。

我的结局是:

if [ -n"$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then

echo Such file exists

fi

您还可以剪切其他文件

if [ -e $( echo $1 | cut -d"" -f1 ) ] ; then

...

fi

这会很慢,因为子壳。如果文件名包含空格怎么办?

在ksh、bash和zsh 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 1st 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

是的,这个闻起来像珍珠。很高兴我没有插手;)

试试这个

fileTarget="xorg-x11-fonts*"

filesFound=$(ls $fileTarget)  # 2014-04-03 edit 2: removed dbl-qts around $(...)

编辑2014-04-03(删除dbl引号并添加测试文件"charlie 22.html"(2个空格)

case ${filesFound} in

"" ) printf"NO files found for target=${fileTarget}

" ;;

* ) printf"FileTarget Files found=${filesFound}

" ;;

esac

试验

fileTarget="*.html"  # where I have some html docs in the current dir

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*

注意,这只在当前目录中有效,或者var fileTarget包含您要检查的路径。

如果fileTarget包含空白(如fileTarget="my file*"),代码将失败。

@理查德汉森有空白的时候,解决办法是什么?

@罗斯:用公认的答案:if ls"my file"* >/dev/null 2>&1; then ...。

@理查德汉森,谢谢,对不起-不为我工作。现在就把它修好。

@罗斯,我添加了一个编辑到我的应该与文件与空间。基本上是case"${filesFound}" in ....。祝大家好运。

根据case的posix规范,case后面的单词不受字段拆分或路径名扩展的影响,因此您添加的双引号无效。问题在于ls $fileTarget中的场分裂。

@罗斯,理查德汉森在研究一些事情,DBL的引语并不重要,也许这就是为什么我一开始没有这些引语。很久以前了,我不确定。对于这个问题,我回到了我的测试目录,并创建了一个链接,名为charlie 22.html,我已经编辑了上面的结果。这能处理每一个案件和find ... -print0 | xargs -0 ..一样吗?可能不会。它是否在您控制创建哪些文件名的环境中工作?它对我有好处,我的意思是我经常使用这个构造(在我控制创建的文件名的环境中;-)。祝大家好运。

人体试验

if [ -e file ]; then

...

fi

适用于dirfile。

当做

这不适用于通配符(这是这个问题中的问题)。如果它与多个文件匹配,您将得到bash: [: too many arguments。

有点不公平,因为这在Solaris上非常有效……

嘿,老博文,感谢克里斯的支持——我那时确实也在和Solaris合作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值