如何以编程方式判断文件名是否与 shell glob 模式匹配?

jay*_*ren 17 bash wildcards

我想判断一个字符串$string是否会被一个 glob 模式匹配$pattern$string可能是也可能不是现有文件的名称。我怎样才能做到这一点?

假设我的输入字符串采用以下格式:

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"
Run Code Online (Sandbox Code Playgroud)

我想找到一个bash成语,决定是否$string将被匹配$pattern1$pattern2或任何其它任意的glob模式。这是我迄今为止尝试过的:

  1. [[ "$string" = $pattern ]]

    这几乎有效,除了$pattern被解释为字符串模式而不是全局模式。

  2. [ "$string" = $pattern ]

    这种方法的问题$pattern是 展开然后在$string和 的展开之间执行字符串比较$pattern

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    这个有效,但前提是$string包含存在的文件。

  4. [[ $string =~ $pattern ]]

    这不起作用,因为=~运算符导致$pattern被解释为扩展的正则表达式,而不是 glob 或通配符模式。

jay*_*ren 10

这个问题没有通用的解决方案。原因是,在 bash 中,大括号扩展(即,{pattern1,pattern2,...}和文件名扩展(又名 glob 模式)被认为是独立的东西,并在不同条件和不同时间扩展。这是 bash 执行的扩展的完整列表:

  • 大括号扩展
  • 波浪号扩展
  • 参数和变量扩展
  • 命令替换
  • 算术展开
  • 分词
  • 路径扩展

由于我们只关心其中的一个子集(可能是大括号、波浪号和路径名扩展),因此可以使用某些模式和机制以可控方式限制扩展。例如:

#!/bin/bash
set -f

string=/foo/bar

for pattern in /foo/{*,foo*,bar*,**,**/*}; do
    [[ $string == $pattern ]] && echo "$pattern matches $string"
done
Run Code Online (Sandbox Code Playgroud)

运行此脚本会生成以下输出:

/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为set -f禁用了路径名扩展,因此语句中只发生大括号扩展和波浪号扩展for pattern in /foo/{*,foo*,bar*,**,**/*}。然后,我们可以[[ $string == $pattern ]]在执行大括号扩展后使用测试操作 来测试路径名扩展。


mik*_*erv 9

我不相信这{bar,baz} 一个 shell glob 模式(虽然肯定/foo/ba[rz]是)但是如果你想知道是否$string匹配,$pattern你可以这样做:

case "$string" in 
($pattern) put your successful execution statement here;;
(*)        this is where your failure case should be   ;;
esac
Run Code Online (Sandbox Code Playgroud)

您可以随心所欲地进行:

case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*)         still no match;;
esac
Run Code Online (Sandbox Code Playgroud)

  • +1 这完全按照标题和第一句话回答了问题。尽管这个问题后来将其他模式与 shell glob 模式混为一谈。 (3认同)