Bash 运算符=~
等同于perl
调用吗?
filename="test-33.csv"
regex="([^.]+)(-\d{1,5})(\.csv)"
使用 bash 测试:
if [[ "$filename" =~ $regex ]]; then echo "it matches"; else echo "doesn't match"; fi
# doesn't match
if [[ "$filename" =~ ([^.]+)(-\d{1,5})(\.csv) ]]; then echo "matches"; else echo "doesn't match"; fi
# doesn't match
和perl
:
result="$(perl -e "if ('$filename' =~ /$regex/) { exit 0;} else { exit 1;} ")"
if [[ result ]]; then echo "it matches"; else echo "doesn't match"; fi
# it matches
我遗漏了 bash=~
运算符的什么吗?这与贪婪与非贪婪迭代器 () 有关吗[^.]+
?
答案1
正则表达式有几种不同的类型,每种类型都添加了更多的运算符(因此,如果要将更多的字符视为文字,则需要对它们进行转义)。
该=~
操作符在文档中描述man bash
如下(请参阅您的系统或在线文档),
还有一个二元运算符
=~
,其优先级与==
和相同!=
。使用时,运算符右侧的字符串被视为POSIX 扩展正则表达式并进行相应匹配
grep -E
扩展正则表达式 (ERE) 可以与(以前称为)匹配egrep
。您的示例是 Perl 兼容正则表达式 (PCRE),它是 ERE 的超集,不能与 一起使用。但是,可以通过将其替换为来=~
轻松适应:\d
[[:digit:]]
echo abc-123.csv | grep -E '([^.]+)(-\d{1,5})(\.csv)' # ERE fails
echo abc-123.csv | grep -P '([^.]+)(-\d{1,5})(\.csv)' # PCRE matches with GNU grep
echo abc-123.csv | grep -E '([^.]+)(-[[:digit:]]{1,5})(\.csv)' # ERE matches modified expression
因此,鉴于grep -E
相当于=~
我们可以这样写,
if [[ "$filename" =~ ([^.]+)(-[[:digit:]]{1,5})(\.csv) ]]
then
echo "matches"
else
echo "doesn't match"
fi
请注意,您的 ERE 可能应该以 为前缀^
,以 为后缀$
,并且[^.]+
调整为[^-.]+
以确保您无法匹配如下字符串abc-def-12345678-123.csv.txt
:
^[^-.]+-[[:digit:]]{1,5}\.csv$
如果您坚决使用 PCRE 而不是 ERE,则必须使用外部工具(例如的 GNU 实现)来grep
执行匹配。但这样做效率较低,并且此处适用与上述相同的关于边界的建议:
if echo "$filename" | grep -qP '([^.]+)(-\d{1,5})(\.csv)'
then
echo "matches"
else
echo "doesn't match"
fi
基本 RE(RE 或 BRE)和 ERE 的 POSIX 参考位于https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html,Perl REs (PCRE) 的参考资料位于https://www.pcre.org/original/doc/html/pcrepattern.html。请注意,这两份文档都不是容易理解的。
最后,你问,
这与贪婪和非贪婪迭代器有关吗
([^.]+)
?
这不是一个贪婪/非贪婪的迭代器。[^.]+
是贪婪的,意味着“一个或多个除点 ( .
)之外的任意字符“。ERE 没有非贪婪运算符。PCRE 可以定义一个非贪婪运算符,例如*
或 ,+
方法是在其后加上?
。例如对比a*
和a*?
;第一个将匹配a
尽可能多的字符,而第二个将匹配尽可能少的字符。
括号( … )
是一种分组,而不是贪婪指示器。
答案2
=~
中的运算符Bash shell
相当于grep -E
GNU 命令。Perl
正则表达式无法被它识别。您需要执行以下操作:
~$ [ $(echo "$filename" | grep -Po "$regex") ] && echo "it matches" || echo "does not match"
it matches
具有等价物。
关于grep
使用的选项:
-o, --only-matching show only the part of a line matching PATTERN
-P, --perl-regexp PATTERN is a Perl regular expression
您的原始形式如下所示:
if [[ $(echo "$filename" | grep -Po "$regex") ]]; then echo "it matches"; else echo "does not match"; fi
这也有效:
if [ $(echo "$filename" | grep -Po "$regex") ]; then echo "it matches"; else echo "does not match"; fi
您还可以执行以下操作:
yyy@xxx:~$ filename="test-33.csv"
yyy@xxx:~$ regex="([^.]+)(-\d{1,5})(\.csv)"
yyy@xxx:~$ result=$(echo "$filename" | grep -Po "$regex")
yyy@xxx:~$ if [[ $result ]]; then echo "it matches"; else echo "does not match"; fi
it matches
yyy@xxx:~$
答案3
Bash 有扩展的 glob 模式它更接近正则表达式。运算符内部[[...]]
进行==
glob 样式的模式匹配。
filename=test-33.csv
# one or more non-dots, a hyphen, a digit, optionally 4 more digits, the extension
pattern='+([^.])-[0-9]?([0-9])?([0-9])?([0-9])?([0-9]).csv'
[[ $filename == $pattern ]] && echo Y || echo N
如果您使用正则表达式来过滤文件名列表,请改用 for 循环中的 glob 模式。
shopt -s extglob
for file in $pattern; do
# do something with the file.
echo "$file"
done
笔记
- 命令
shopt
:extended glob 在其中自动启用[[...]]
,但在其他情况下则不启用。 $pattern
在这些代码片段中特意不加引号,以便它被处理为一种模式而不是文字字符串。