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

Ken*_*oom 213 bash glob

如果我想检查是否存在单个文件,我可以使用test -e filename或测试它[ -e filename ].

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

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

fla*_*let 163

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

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

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

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

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

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

如果发现感觉像是矫枉过正,并且可能匹配的文件数量很少,请使用stat:

if stat -t glob* >/dev/null 2>&1
then
    echo found
else
    echo not found
fi
Run Code Online (Sandbox Code Playgroud)

  • `find`似乎完全正确.它没有极端情况,因为shell没有进行扩展(并且将未扩展的glob传递给其他命令),它可以在shell之间移植(虽然显然不是你使用的所有选项都是由POSIX指定的),并且它比`ls -d glob*`(之前接受的答案)因为它在第一场比赛时停止了. (9认同)

Bri*_*man 162

Bash特定解决方案:

compgen -G "<glob-pattern>"
Run Code Online (Sandbox Code Playgroud)

逃离模式或它将被预先扩展为匹配.

退出状态是:

  • 1表示不匹配,
  • 0表示"一场或多场比赛"

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

更新:请求的示例用法.

if compgen -G "/tmp/someFiles*" > /dev/null; then
    echo "Some files exist."
fi
Run Code Online (Sandbox Code Playgroud)

  • 请注意,`compgen`是特定于_bash_的内置命令,不是POSIX标准Unix shell指定的内置命令的一部分.http://pubs.opengroup.org/onlinepubs/9699919799/ http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14因此,请避免在脚本中使用它,因为其他shell的可移植性是一个问题. (8认同)
  • 在我看来,没有 bash 内置函数的类似效果是使用任何其他作用于 glob 的命令,如果没有匹配的文件,则失败,例如 ls: `if ls /tmp/*Files 2&gt;&amp;1 &gt;/dev/null ; 那么 echo 存在;fi` - 也许对代码高尔夫有用?如果有一个与 glob 同名的文件,该 glob 不应该匹配,则失败,但如果是这种情况,您可能会遇到更大的问题。 (4认同)
  • @DewiMorgan:您的建议是: if ls /tmp/*Files 2&gt;&amp;1 &gt;/dev/null; 那么回声存在;fi 基本上是一个不错的选择,但是重定向不会像您想要的那样工作: if ls /tmp/*Files &gt;/dev/null 2&gt;&amp;1 ; 那么回声存在;菲 (3认同)
  • @DewiMorgan这更简单:`if ls / tmp / * Files&&gt; / dev / null; 然后回声存在;fi (2认同)
  • 是的,引用它,否则文件名通配符将被预先扩展。compgen“ dir / *。ext” (2认同)

mik*_*iku 24

#!/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
Run Code Online (Sandbox Code Playgroud)

  • 为了避免可能的错误“不匹配”,请设置“ nullglob”,而不要检查单个结果是否等于模式本身。某些模式可以匹配与模式本身完全相同的名称(例如,“ a * b”;但不能匹配例如“ a?b”或“ [a]”)。 (2认同)

Dan*_*och 19

我喜欢

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

if exists glob*; then
    echo found
else
    echo not found
fi
Run Code Online (Sandbox Code Playgroud)

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

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

  • 如果glob类似于`*.[cC]`(可能没有`c`或`C`文件,但是文件叫做`*.[cC]`)或假阴性,如果从那扩展的第一个文件例如是一个符号链接到一个不存在的文件或一个你无法访问的目录中的文件(你想添加一个`|| [-L"$ 1"]`). (12认同)

Mag*_*ero 9

如果您设置了globfail,则可以使用这个疯狂选项(您实际上不应该这样做)

shopt -s failglob # exit if * does not match 
( : * ) && echo 0 || echo 1
Run Code Online (Sandbox Code Playgroud)

要么

q=( * ) && echo 0 || echo 1
Run Code Online (Sandbox Code Playgroud)

  • 完美使用noop失败。永远不要使用...但是真的很漂亮。:) (2认同)

小智 8

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

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

if globexists glob*; then
    echo found
else
    echo not found
fi
Run Code Online (Sandbox Code Playgroud)

  • 这仍然无法修复那些在其中包含全局特殊字符的文件名,例如Stephane Chazelas指出Dan Bloch的回答.(除非你用nullglob猴子). (4认同)
  • 你应该在`test` /`[`中避免`-o`和`-a`.例如,在这里,如果大多数实现的`$ 1`是`=`,它就会失败.使用`[ - e"$ 1"] || [-L"$ 1"]`而不是. (2认同)

小智 5

我还有另一个解决方案:

if [ "$(echo glob*)" != 'glob*' ]
Run Code Online (Sandbox Code Playgroud)

这对我很好。我想念一些极端的情况吗?

  • 除非文件实际命名为“glob*”,否则有效。 (2认同)