我想知道字符串是否$string
与全局模式匹配$pattern
。 $string
可能是也可能不是现有文件的名称。我怎样才能做到这一点?
假设我的输入字符串采用以下格式:
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"
我想找到一个 bash 习惯用法来确定是否与、或任何其他任意全局模式$string
匹配。这是我到目前为止所尝试过的:$pattern1
$pattern2
[[ "$string" = $pattern ]]
这几乎有效,除了被
$pattern
解释为字符串模式而不是全局模式。[ "$string" = $pattern ]
这种方法的问题是被扩展,然后在和 的扩展
$pattern
之间执行字符串比较。$string
$pattern
[[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]
此方法有效,但前提是
$string
包含存在的文件。[[ $string =~ $pattern ]]
这不起作用,因为
=~
运算符会$pattern
被解释为扩展正则表达式,而不是全局或通配符模式。
答案1
这个问题没有通用的解决方案。原因是,在 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
运行此脚本会生成以下输出:
/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar
这是有效的,因为set -f
禁用了路径名扩展,因此语句中仅出现大括号扩展和波形符扩展for pattern in /foo/{*,foo*,bar*,**,**/*}
。然后[[ $string == $pattern ]]
,在执行大括号扩展后,我们可以使用测试操作 来测试路径名扩展。
答案2
我不相信{bar,baz}
是shell 全局模式(虽然肯定/foo/ba[rz]
是)但如果你想知道是否$string
匹配$pattern
,你可以这样做:
case "$string" in
($pattern) put your successful execution statement here;;
(*) this is where your failure case should be ;;
esac
你可以做任意多的事情:
case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*) still no match;;
esac
答案3
正如帕特里克指出的,你需要一种“不同类型”的模式:
[[ /foo/bar == /foo/@(bar|baz) ]]
string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]
那里不需要引号。
答案4
如果它是关于检查是否$string
在由存储的 glob 扩展产生的文件路径中$pattern
(请记住,正如其他人所说,{foo,bar}
这不是 glob 运算符),那么使用zsh
,您可以执行以下操作:
if ()(($#)) $~pattern(NY1e['[[ $REPLY = $string ]]']); then
print -r -- "$string is among the result of the $pattern glob expansion"
fi
使用bash
,您始终可以使用循环:
among() (
string=$1 pattern=$2 IFS=
shopt -s nullglob extglob
for file in $pattern@(); do
[[ "$string" = "$file" ]] && return
done
false
)
if among "$string" "$pattern"; then
printf '%s\n' "$string is among the result of the $pattern glob expansion"
fi
(extglob
启用后可以启用 ksh 扩展运算符的子集,您可以使用诸如 之类的东西foo/@(foo|bar)
。我们添加@()
到上面的模式以强制全局扩展;没有它,among foo foo
即使foo
不存在也会返回 true。但这意味着以/
don结尾的模式不起作用(如among /bin/ '/*/'
)。
在 中ksh
,只要noglob
未启用且未braceexpand
禁用,除了 split+glob 之外,在参数扩展时也会执行大括号扩展。ksh93
还有一个~(N)
glob 运算符相当于zsh
/bash
的nullglob
,所以你可以这样做:
function among {
typeset string="$1" pattern="$2" IFS= file
set -o braceexpand +o noglob
for file in ~(N)$pattern; do
[[ "$string" = "$file" ]] && return
done
false
}
among foo/bar 'foo/{bar,baz}'
只要文件存在就会返回true foo/bar
。
请注意,ksh 的扩展 glob 运算符在其他扩展的结果中时不会被识别(以便按照 POSIX 要求a='@(x)'; echo $a
输出)。@(x)
请注意,在所有这些中,即使字符串与模式匹配,among .foo '*'
or among /etc/issue '*'
or也会返回 false。among /usr/local/bin '/*/bin'