在 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"
加了双引号,不再当成正则表达式处理,只会比较字符串自身的内容。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。