在 bash 的 if 条件中使用正则表达式

Posted

技术标签:

【中文标题】在 bash 的 if 条件中使用正则表达式【英文标题】:use regular expression in if-condition in bash 【发布时间】:2011-01-21 20:19:01 【问题描述】:

我想知道在 bash 的 if 子句中使用正则表达式的一般规则?

这是一个例子

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

为什么最后三个不匹配?

希望你能给出尽可能多的通用规则,而不仅仅是这个例子。

【问题讨论】:

【参考方案1】:
if [[ $gg =~ ^....grid.* ]]

【讨论】:

您应该可以使用“.4”而不是“....”,即“^.4grid.*”。它可以更容易阅读和理解。【参考方案2】:

使用 =~

对于正则表达式检查Regular Expressions Tutorial Table of Contents

【讨论】:

【参考方案3】:

使用 glob 模式时,问号表示单个字符,星号表示零个或多个字符的序列:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

使用正则表达式时,点表示单个字符,星号表示前面的零个或多个字符。所以“.*”代表零个或多个任意字符,“a*”代表零个或多个“a”,“[0-9]*”代表零个或多个数字。另一个有用的(在许多中)是加号,它代表一个或多个前面的字符。因此“[a-z]+”代表一个或多个小写字母字符(在 C 语言环境中 - 以及其他一些语言环境中)。

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi

【讨论】:

那么字符串匹配有两种方式:glob模式和正则表达式? glob pettern 不仅用于文件名吗?在 bash 中,何时使用 glob 模式以及何时使用正则表达式?谢谢! @Tim: Globbing 在大多数或所有版本的 Bash 中都可用。正则表达式匹配仅在版本 3 及更高版本中可用,但我建议仅在 3.2 及更高版本中使用它。正则表达式比通配符更通用。【参考方案4】:

@OP,

glob pettern 不仅用于文件名吗?

不,“glob”模式不仅用于文件名。您也可以使用它来比较字符串。在您的示例中,您可以使用 case/esac 来查找字符串模式。

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

在 bash 中,什么时候使用 glob 模式,什么时候使用正则表达式?谢谢!

Regex 比“glob 模式”更通用和“方便”,但是除非您正在执行“globbing/extended globbing”无法轻松提供的复杂任务,否则没有必要使用 regex。 bash extglob )。有关扩展通配符,请参阅 here 和一些简单的示例 here。

OP 更新:使用正则表达式查找以 2 个字符(点“.”表示 1 个字符)后跟“g”的文件的示例

例如输出

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

在上面,文件被匹配是因为它们的名称包含 2 个字符,后跟“g”。 (即..g)。

globbing 的等效项是这样的:(查看reference 以了解?* 的含义)

$ for file in ??g*; do echo $file; done
abg
degree
..g

【讨论】:

感谢 ghostdog74。在高于 3.2 版本的 Bash 中,可以使用正则表达式来替换 glob 模式出现的任何地方吗?还是正则表达式只能在某些特殊情况下使用?例如,我发现“ls ??g”正在工作,而“ls ..g”却没有。 如果有需要,你可以使用正则表达式。由你决定。请注意,正则表达式语法与 shell 通配符语法不同。所以ls ..g 不起作用。您是在告诉 shell 查找名为 ..g 的文件。至于学习regex语法,可以试试perldoc perlretutperldoc perlrequick,或者在命令行下info sed【参考方案5】:

使用grep 和基本的sh 内置函数为那些对更便携的解决方案感兴趣的人添加这个解决方案(独立于bash 版本;也适用于普通的旧sh,在非Linux 平台等)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

一些grep 化身还支持-q(安静)选项作为重定向到/dev/null 的替代方案,但重定向又是最便携的。

【讨论】:

忘记了 egrep 的结束符 ")" 使用grep -q 而不是grep >/dev/null

以上是关于在 bash 的 if 条件中使用正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

Bash if 语句中的正则表达式匹配

如何在 bash 脚本中使用正则表达式?

如何在 bash 脚本中使用正则表达式否定测试?

shell脚本:if条件判断中使用正则表达式

为啥你需要 (?(R)...|...) if 正则表达式中的条件?

Perl学习笔记-2(正则表达式)