在 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' 加了双引号,不再当成正则表达式处理,只会比较字符串自身的内容。
|