解惑 [[ ]] 中 ==、!= 以及 =~、!~ 的使用及其「怪异」行为

解惑 [[ ]] 中 ==、!= 以及 =~、!~ 的使用及其「怪异」行为

bash 中支持 [[ string == pattern ]] 这样的用法, 其中的 pattern 被解释成 glob 风格的通配符. 比如, 要判断一个文件名是否具有 .txt 后缀, 可以这样用:
引用:
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; fname=file1.txt
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; if [[ $fname == *.txt ]]; then echo yes; else echo no; fi
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; fname=file1.jpg
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; if [[ $fname == *.txt ]]; then echo yes; else echo no; fi
no
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; bye
但是, 如果 pattern 是被单引号或者双引号引起来的, 则 == 的行为就跟 = 相同了, 也就是说这个时候进行的是简单的字符串比较操作而不是 pattern matching:
引用:
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; fname=file1.txt
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; if [[ $fname == '*.txt' ]]; then echo yes; else echo no; fi
no
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; fname='*.txt'
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; if [[ $fname == '*.txt' ]]; then echo yes; else echo no; fi
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; bye
!= 跟 == 类似, 只不过其结果跟 == 相反.

      
在 bash 3.0 中新引入了 [[ string =~ pattern ]] 以及 [[ string !~ pattern ]] 的用法, 不过这里的 pattern 被解释成 extended regular expression 而不是 glob 风格的 pattern, 而且 pattern 是否被引号引起来没有区别:
引用:
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; echo $BASH_VERSION
3.00.14(1)-release
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; fname=file1.txt
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; [[ $fname =~ '.*\.txt$' ]] && echo yes || echo no
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; [[ $fname =~ .*\.txt$ ]] && echo yes || echo no
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; fname=file1.jpg
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; [[ $fname =~ '.*\.txt$' ]] && echo yes || echo no
no
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=19493 $?=0] ; bye
!~ 跟 =~ 用法类似, 只不过结果跟 =~ 相反

      
这里的==称为操作符好像不妥,==是另一层测试命令(TEST-COMMANDS)中可以看似操作符.

理解对不?

      
在 bash 3.2 中, 作者做了一个「愚蠢」的决定: 在使用 [[ string =~ pattern ]] 时, 如果 pattern 被加了引号, 则 =~ 不会把 pattern 解释成 ERE, 这时其行为也是简单的字符串比较操作 为了解决这种不兼容性, 3.2.39 中引入了一个新的 shopt 设置: compat31, compat31 默认是 disable 的, 当其 enable 后, =~ 的行为就跟 3.0/3.1 兼容了:
引用:
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; echo $BASH_VERSION
3.2.39(1)-release
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; [[ file1.txt =~ '.*\.txt$' ]] && echo yes || echo no
no
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; [[ file1.txt =~ .*\.txt$ ]] && echo yes || echo no
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; shopt -s compat31
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; [[ file1.txt =~ '.*\.txt$' ]] && echo yes || echo no
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; [[ file1.txt =~ .*\.txt$ ]] && echo yes || echo no
yes
-(dearvoid@LinuxEden:Forum)-(~/tmp)-
[$$=22971 $?=0] ; bye
      
引用:
原帖由 blackspace 于 2008-10-24 11:07 发表
这里的==称为操作符好像不妥,==是另一层测试命令(TEST-COMMANDS)中可以看似操作符的参数。

理解对不?
bash manual 里面是叫 operator 的, 不知道翻译成「操作符」是否妥当?      
确实操作符,同时确实不是操作符。

bash操作符严格说控制操作符和重定向操作符。

这个是手册中让人迷惑的地方。

需要一个提醒吧。      

这是怎么回事,版主能告诉我么。bash的版本号是2.05,不论字符串的两个比较运算符==(=),!=怎么比较结果都是成功。我都快崩溃了。      
>fname=file1.txt
>if [[ fname='*.txt' ]];then echo yes;else echo no; fi
>yes      
引用:
原帖由 apsnowolf 于 2008-10-30 10:04 发表
>fname=file1.txt
>if [[ fname='*.txt' ]];then echo yes;else echo no; fi
>yes
bash 中引用变量要在变量名前加 $ 符号的
--
[ ] 或者 [[ ]] 中的操作符两边是要有空格的      
不好意思,是笔误:实际是这样:
>fname=file1.txt
>if [[ $fname='*.txt' ]];then echo yes;else echo no;fi
>yes
............................................................
>var="zhongguo"
>[[ $var="meiguo" ]]
>echo $?
>0
............................................................