分享

Bash技巧:使用[[命令的 =~ 操作符判断字符串的包含关系

 苏醒的贝壳 2020-01-12

Bash技巧:使用[[命令的 =~ 操作符判断字符串的包含关系

在 Linux bash shell 中,可以使用 [[ 命令来进行判断。
其中,可以使用 [[命令的 =~操作符来判断某个字符串是否包含特定模式。

查看 man bash 对 [[ 命令的 =~ 操作符说明如下:

An additional binary operator, =~, is available, with the same precedence as == and !=.
When it is used, the string to the right of the operator is considered an extended regular expression and matched accordingly.
The return value is 0 if the string matches the pattern, and 1 otherwise.
If the regular expression is syntactically incorrect, the conditional expression's return value is 2.

Any part of the pattern may be quoted to force the quoted portion to be matched as a string.

即,使用 =~ 操作符时,其右边的字符串被认为是一个扩展正则表达式。
扩展之后跟左边字符串进行比较,看左边字符串是否包含指定模式。
注意是包含关系,不是完整匹配。
也就是判断右边的模式是否为左边字符串的子字符串,而不是判断右边的模式是否完全等于左边字符串。

这里面提到一个非常关键的点,在所给的扩展正则表达式中,用引号括起来的部分会被当成字符串,不再被当成正则表达式。
如果 =~ 操作符右边的字符串都用引号括起来,那么表示匹配这个字符串自身的内容,不再解析成正则表达式。

如果想要 =~ 操作符右边的字符串被当成正则表达式来处理,一定不能用引号把整个字符串括起来。
无论是双引号、还是单引号都不要加。
这是常见的使用误区,后面会举例说明。

注意:只有 [[ 命令支持 =~ 操作符,test 命令和 [ 命令都不支持 =~ 操作符。

判断字符串是否全是数字

下面用 =~ 操作符来判断一个字符串是否全是数字。

假设有一个 checkdigits.sh 脚本,内容如下:

(不好意思,近期网页版文章的代码块排版错乱,后台咨询确认网页版不支持。下面用四个 ‘----’ 代替四个空格来进行缩进和隔行显示。后面的代码块会类似处理。如果需要复制代码到本地验证,麻烦以四个 ‘----’为单位,替换成四个空格。非常抱歉。)

#!/bin/bash

function check_digits()

{

----local count=${#1}

----if [[ '$1' =~ [0-9]{$count} ]]; then

--------echo 'All digits.'

----else

--------echo 'Not all digits.'

----fi

}

check_digits '$1'

该脚本定义了一个 check_digits 函数。
这个函数使用 ${#1}参数扩展表达式获取所传入第一个参数的字符串长度,并赋值给 count 变量。

在正则表达式中,[0-9] 表示匹配 0 到 9 之间的任意一个数字,但是只匹配一个数字。
[0-9]{n} 表示匹配 n 个连续的数字。

[[ '$1' =~ [0-9]{$count} ]] 表达式中,用 =~ 操作符判断第一个参数值是否精确匹配 count 个连续的数字。
如果是,就说明第一个参数对应的字符串全是数字,否则不全是数字。

执行 checkdigits.sh` 脚本的结果如下:

$ ./checkdigits.sh 12345

All digits.

$ ./checkdigits.sh abcd

Not all digits.

$ ./checkdigits.sh a2c

Not all digits.

$ ./checkdigits.sh 1b3

Not all digits.

可以看到,传入的参数全是数字时,才会打印 'All digits.'。
传入全字母、或者字母和数字的组合,能够正确判断到不全是数字,会打印 'Not all digits.'。

由于 =~ 操作符右边的参数是扩展正则表达式,如果不熟悉正则表达式的话,在使用时会遇到一些不预期的异常。
下面举例说明判断字符串是否全是数字的一些错误写法,注意避免出现这类错误。

错误写法一

假设有一个 checkdigits_fake.sh 脚本,内容如下:

#!/bin/bash

function check_digits()

{

----if [[ '$1' =~ [0-9] ]]; then

--------echo 'All digits.'

----else

--------echo 'Not all digits.'

----fi

}

check_digits '$1'

这个脚本在 =~ 操作符右边提供的正则表达式是 [0-9],对应 0 到 9 之间任意一个数字,但是只对应一个数字。
那么 [[ '$1' =~ [0-9] ]] 是判断传入的第一个参数是否包含一个数字。
只要有一个数字,就会返回为 true。
它并不能判断出所有字符是否都是数字。

具体执行结果如下:

$ ./checkdigits_fake.sh 12345

All digits.

$ ./checkdigits_fake.sh abcd

Not all digits.

$ ./checkdigits_fake.sh 1b3d

All digits.

$ ./checkdigits_fake.sh a2

All digits.

可以看到,只有当传入的参数全是字母时,才会打印 'Not all digits.'。
传入全数字、或者数字和字母的组合,都会打印 'All digits.'。
这个脚本不能准确地判断字符是否全是数字。

错误写法二

把 checkdigits_fake.sh 脚本修改成下面的内容:

#!/bin/bash

function check_digits()

{

----if [[ '$1' =~ [0-9]* ]]; then

--------echo 'All digits.'

----else

--------echo 'Not all digits.'

----fi

}

check_digits '$1'

即,用 [0-9]* 来表示匹配零个或多个连续的数字。

从字面上看像是可以匹配到全是数字的情况。
但实际上,它还是会匹配一个数字的情况。
只要有一个数字就会认为匹配,甚至还会匹配没有数字的情况。

具体的执行结果如下:

$ ./checkdigits_fake.sh 12345

All digits.

$ ./checkdigits_fake.sh abcd

All digits.

$ ./checkdigits_fake.sh 1b3d

All digits.

$ ./checkdigits_fake.sh a2

All digits.

可以看到,无论传入的参数是全数字、全字母、还是数字和字母的组合,都是打印 'All digits.',都符合所给的 [0-9]* 这个模式。 即,这个脚本也达不到判断字符串是否全是数字的效果。

类似的,[0-9]+ 表示匹配一个或多个连续的数字。
使用这个模式也不能判断字符串是否全是数字。

错误写法三

前面提到,如果把 =~ 操作符右边的字符串都用双引号括起来,那么表示匹配这个字符串自身的内容,不再解析成正则表达式。
例如 [0-9] 在正则表达式中对应一个数字。
但是 '[0-9]' 对应的是 '[0-9]' 这个字符串,不再对应一个数字。

虽然上面的 [[ '$1' =~ [0-9]{$count} ]] 表达式可以正确判断出字符串是否都是数字。
一旦用双引号把 [0-9]{$count} 括起来,写成 [[ '$1' =~ '[0-9]{$count}' ]],就会判断出错。
可以自行修改 checkdigits.sh 脚本代码进行验证。

下面用其他例子进行举例说明:

$ [[ '123' =~ [0-9]{3} ]]; echo $?

0

$ [[ '123' =~ '[0-9]{3}' ]]; echo $?

1

$ [[ '[0-9]{3}' =~ [0-9]{3} ]]; echo $?

1

$ [[ '[0-9]{3}' =~ '[0-9]{3}' ]]; echo $?

0

可以看到,[[ '123' =~ [0-9]{3} ]] 正确地判断出 '123' 字符串包含三个连续的数字。
用 echo $? 打印命令返回值是 0,也就是 true。

[[ '123' =~ '[0-9]{3}' ]]命令的返回值是 1,对应 false,认为要比较的两个字符串不匹配。

'[0-9]{3}' 此时不再表示匹配三个连续的数字,而是匹配 '[0-9]{3}' 这个字符串自身。

[[ '[0-9]{3}' =~ [0-9]{3} ]] 命令中,右边的 [0-9]{3} 没加双引号,会按照正则表达式来解析,表示匹配三个连续的数字。
而左边字符串并没有三个连续的数字,所以返回 1,不匹配。

[[ '[0-9]{3}' =~ '[0-9]{3}' ]]命令中,右边的 '[0-9]{3}' 加了双引号,不再当成正则表达式处理。
这只会比较字符串自身,所以返回 0,是匹配的。

在 bash 中,为了避免单词拆分导致不预期的行为,一般都会用双引号把字符串、或者变量值括起来。
但是在使用 =~ 操作符时,注意检查右边字符串是否要当成正则表达式来处理。
如果是,不要加双引号。

判断某个字符串是否为另一个字符串的子字符串

我们可以使用 =~ 操作来判断某个字符串是否为另一个字符串的子字符串。
要判断的字符串要写在操作符右边,被判断的字符串要写在操作符的左边。

假设有一个 check_substr.sh 脚本,内容如下:

#!/bin/bash

function check_substr()

{

----if [[ '$1' =~ '$2' ]]; then

--------echo \'$1\' contains \'$2\'

----else

--------echo \'$1\' does not contain \'$2\'

----fi

}

check_substr '$1' '$2'

这个脚本判断传入的第二个参数是否为第一个参数的子字符串。

具体执行结果如下:

$ ./check_substr.sh 'This is a test string' 'test string'

'This is a test string' contains 'test string'

$ ./check_substr.sh 'This is a test string' 'is a test'

'This is a test string' contains 'is a test'

$ ./check_substr.sh 'This is a test string' 'isa test'

'This is a test string' does not contain 'isa test'

$ ./check_substr.sh 'This is a test string' 'new string'

'This is a test string' does not contain 'new string'

测试的时候,如果传入的字符串参数包含空格,要用双引号括起来。
注意:=~ 右边的 '$2' 加了双引号,不再当成正则表达式处理,只会比较字符串自身的内容。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多