测试一个glob是否在bash中有任何匹配

时间:2010-05-30 03:22:47

标签: bash glob

如果我想检查是否存在单个文件,我可以使用test -e filename[ -e filename ]对其进行测试。

假设我有一个glob,我想知道是否存在名称与glob匹配的文件。 glob可以匹配0个文件(在这种情况下我不需要做任何事情),或者它可以匹配1个或多个文件(在这种情况下我需要做一些事情)。如何测试glob是否有匹配? (我不关心有多少匹配,如果我能用一个if语句并且没有循环(仅仅因为我觉得最可读),这将是最好的。

(如果glob匹配多个文件,则test -e glob*会失败。)

21 个答案:

答案 0 :(得分:151)

nullglob shell选项确实是一种基础。

为了避免需要对nullglob状态进行繁琐的保存和恢复,我只在扩展glob的子shell中设置它:

if test -n "$(shopt -s nullglob; echo glob*)"
then
    echo found
else
    echo not found
fi

为了更好的可移植性和更灵活的通配,请使用find:

if test -n "$(find . -maxdepth 1 -name 'glob*' -print -quit)"
then
    echo found
else
    echo not found
fi

明确的 -print -quit 操作用于查找,而不是默认的隐式 -print 操作,以便查找将在找到与搜索条件匹配的第一个文件后立即退出。在大量文件匹配的情况下,这应该比echo glob*ls glob*快得多,并且它还避免了扩展命令行过度填充的可能性(某些shell具有4K长度限制)。

如果发现感觉过度杀戮并且可能匹配的文件数量很少,请使用stat:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi

答案 1 :(得分:139)

  

Bash 具体解决方案:

compgen -G "<glob-pattern>"

逃离模式,或者它会预先扩展为匹配项。

退出状态为:

  • 1表示不匹配,
  • 0表示&#39;一场或多场比赛&#39;

stdout是与匹配的文件列表 我认为这是简洁性和最小化潜在副作用的最佳选择。

更新:请求的使用示例。

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi

答案 2 :(得分:21)

#!/usr/bin/env bash

# If it is set, then an unmatched glob is swept away entirely -- 
# replaced with a set of zero words -- 
# instead of remaining in place as a single word.
shopt -s nullglob

M=(*px)

if [ "${#M[*]}" -ge 1 ]; then
    echo "${#M[*]} matches."
else
    echo "No such files."
fi

答案 3 :(得分:17)

我喜欢

exists() {
    [ -e "$1" ]
}

if exists glob*; then
    echo found
else
    echo not found
fi

这既可读又有效(除非有大量文件) 主要的缺点是它比它看起来更微妙,我有时会觉得不得不添加一个长评论。
如果匹配,则"glob*"由shell扩展,并且所有匹配都将传递给exists()"glob*"将检查第一个匹配并忽略其余匹配。
如果没有匹配项,exists()会传递给{{1}},并且发现它不存在。

编辑:可能存在误报,请参阅comment

答案 4 :(得分:6)

测试-e有一个不幸的警告,它认为破碎的符号链接不存在。所以你可能也想检查那些。

function globexists {
  test -e "$1" -o -L "$1"
}

if globexists glob*; then
    echo found
else
    echo not found
fi

答案 5 :(得分:5)

如果你有globfail设置,你可以使用这个疯狂(你真的不应该)

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1

q=( * ) && echo 0 || echo 1

答案 6 :(得分:4)

我还有另一个解决方案:

if [ "$(echo glob*)" != 'glob*' ]

这对我很有用。我想念一些角落案件吗?

答案 7 :(得分:3)

根据他的想法,简化MYYN的答案:

M=(*py)
if [ -e ${M[0]} ]; then
  echo Found
else
  echo Not Found
fi

答案 8 :(得分:3)

基于flabdablet's answer,对我来说,最简单的(不一定是最快的)只是使用 find 本身,同时在shell上保留glob扩展,例如:

find /some/{p,long-p}ath/with/*globs* -quit &> /dev/null && echo "MATCH"

if赞:

if find $yourGlob -quit &> /dev/null; then
    echo "MATCH"
else
    echo "NOT-FOUND"
fi

答案 9 :(得分:1)

在Bash中,你可以为一个数组进行glob;如果glob不匹配,则您的数组将包含与现有文件不对应的单个条目:

#!/bin/bash

shellglob='*.sh'

scripts=($shellglob)

if [ -e "${scripts[0]}" ]
then stat "${scripts[@]}"
fi

注意:如果您设置了nullglobscripts将是一个空数组,您应该使用[ "${scripts[*]}" ][ "${#scripts[*]}" != 0 ]进行测试。如果您正在编写一个必须使用或不使用nullglob的库,那么您需要

if [ "${scripts[*]}" ] && [ -e "${scripts[0]}" ]

这种方法的一个优点是,您可以拥有要使用的文件列表,而不必重复全局操作。

答案 10 :(得分:1)

我没有看到这个答案,所以我想我会把它放在那里:

set -- glob*
[ -f "$1" ] && echo "found $@"

答案 11 :(得分:1)

中类似(测试文件包含pattern):

shopt -s nullglob
compgen -W *pattern* &>/dev/null
case $? in
    0) echo "only one file match" ;;
    1) echo "more than one file match" ;;
    2) echo "no file match" ;;
esac

它比compgen -G更好:因为我们可以区分更多情况,并且更精确。

只能使用一个通配符*

答案 12 :(得分:1)

这种憎恶似乎有效:

#!/usr/bin/env bash
shopt -s nullglob
if [ "`echo *py`" != "" ]; then
    echo "Glob matched"
else
    echo "Glob did not match"
fi

它可能需要bash,而不是sh。

这是有效的,因为如果没有匹配项,则nullglob选项会使glob计算为空字符串。因此,echo命令的任何非空输出都表明glob匹配了什么。

答案 13 :(得分:0)

bash中扩展的glob(extglob)解决方案:

bash -c $'shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'

如果至少有一个匹配项,则退出状态为0,如果没有匹配项,则退出状态为非零(2)。 stdout包含一个以换行符分隔的匹配文件列表(以及文件名中包含用引号引起来的空格)。

或者,略有不同

bash -c $'shopt -s extglob \n compgen -G <ext-glob-pattern>'

与基于ls的解决方案的区别:可能更快(未测量),输出中未引用空格的文件名,没有匹配项时退出代码1(不是2:耸肩:)。

用法示例:

没有匹配项:

$ bash -c $'shopt -s extglob \n /bin/ls -1U @(*.foo|*.bar)'; echo "exit status: $?"
/bin/ls: cannot access '@(*.foo|*.bar)': No such file or directory
exit status: 2

至少一场比赛:

$ bash -c $'shopt -s extglob \n /bin/ls -1U @(*.ts|*.mp4)'; echo "exit status: $?"
'video1 with spaces.mp4'
video2.mp4
video3.mp4
exit status: 0

使用的概念:

  • ls'退出代码行为(为efficiency添加-U,为输出控制添加-1)。
  • 在当前外壳程序中不启用extglob(通常是不希望的)。
  • 使用$前缀,以便解释\n,以便扩展的glob模式与shopt -s extglob不在同一行上,否则扩展的glob模式将是语法错误!

注释1:我之所以努力解决此问题,是因为其他答案中建议的compgen -G "<glob-pattern>"方法似乎不适用于brace expansion;但是我需要一些更高级的环球功能。

注意2::扩展的glob语法的可爱资源:http://mywiki.wooledge.org/glob#extglob

答案 14 :(得分:0)

set -- glob*
if [ -f "$1" ]; then
  echo "It matched"
fi

说明

如果glob*不匹配,则$1将包含'glob*'。测试-f "$1"不正确,因为glob*文件不存在。

为什么这比替代品要好

这适用于sh及其派生词:ksh和bash。它不会创建任何子外壳。 $(..)`...`命令创建一个子外壳;他们分叉了一个过程,因此比该解决方案要慢。

答案 15 :(得分:0)

#!/bin/bash
set nullglob
touch /tmp/foo1 /tmp/foo2 /tmp/foo3
FOUND=0
for FILE in /tmp/foo*
do
    FOUND=$((${FOUND} + 1))
done
if [ ${FOUND} -gt 0 ]; then
    echo "I found ${FOUND} matches"
else
    echo "No matches found"
fi

答案 16 :(得分:0)

流行的观点是 [ -f file* ] 不起作用。事实上,它确实有效,而且我个人认为它在某些情况下非常有用——当我想在特定位置捕获一个且只有一个文件的名称时。例如,名称中包含版本号的文件。考虑以下代码:

if [ -f "$ROOT"/lib64/libc-*.so ] ;then
  LIBC=$(basename -- "$ROOT"/lib64/libc-*.so .so)
else
  echo "libc ??" ; exit 1
fi

顺便说一句,shellcheck 看到这样的用法时会大喊大叫。 :-) 我希望他们能解决这个问题!

https://www.shellcheck.net/wiki/SC2144

答案 17 :(得分:0)

[ls glob* 2>/dev/null | head -n 1]&amp;&amp; echo true

答案 18 :(得分:0)

if ls -d $glob > /dev/null 2>&1; then
  echo Found.
else
  echo Not found.
fi

请注意,如果有很多匹配或文件访问速度很慢,这可能非常耗时。

答案 19 :(得分:-1)

ls | grep -q "glob.*"

不是最有效的解决方案(如果目录中有大量文件可能会变慢),但它简单易读,并且还具有正则表达式比普通版更强大的优势bash glob模式。

答案 20 :(得分:-1)

(ls glob* &>/dev/null && echo Files found) || echo No file found