bash中(),{},(()),[],[[]]的区别
前言:在bash中遇到各种括号,同时在进行字符数值比较判定时,总是不断出现问题,于是通过参考《advanced bash-scripting guide》,同时在centos 6.7版本上进行测试,现况总结如下。如有纰漏,望指正。
一.()
一个命令组合,相当于一个命令组
[root@mghuee~chunlanyy testdir]# I=123;(I=xyz;echo $I;);echo $I xyz 123
二.{}
同"{}",也为一个命令组合,与"()"的区别就是前者在是当前shell中进行,而后者在里面子shell中进行
[root@mghuee~chunlanyy testdir]# I=123;(echo $I;I=xyz;echo $I;);echo $I 123 xyz 123 [root@mghuee~chunlanyy testdir]# I=123;{ echo $I;I=xyz;echo $I; };echo $I 123 xyz xyz
从例子中可以看得,"()"中的赋值只能影响到自身子shell,并不会赋值给父shell,而"{}"则只是在同一shell进行
另外需要注意的是,"{}"是一个keyword,所以在命令与两边的"{"需用空格隔离,同时使用";"表示命令结束。
[root@mghuee~chunlanyy testdir]# type {
{ is a shell keyword
三.[]
"[]"主要用作条件判断,判断对象包括文件类型和赋值比较
[root@mghuee~chunlanyy testdir]# type [
[ is a shell builtin
我们可以看到"["是一个内部命令,基本可认为等同于test命令
[root@mghuee~chunlanyy testdir]# [ 4 -lt 3 ] && echo yes || echo no no [root@mghuee~chunlanyy testdir]# test 4 -lt 3 && echo yes || echo no no
常见的比较测试如下:
数字测试:-eq -ne -lt -le -gt -ge
文件测试:-r、-l、-w、-x、-f、-d、-s、-nt、-ot
字符串测试:=、!=、-n、-z、\>、\<
逻辑测试:-a、-o、!
数学运算:支持整数运算,但已被"((...))"取代
注意的几点:
1.数字比较只能用-lt这样的比较符,而不能使用"<"这样的比较符,即便是加了转义符的"\<"
[root@mghuee~chunlanyy testdir]# [ 1 > 2 ] && echo yes || echo no yes
很显然,"1>2"应该是错误的,但是显示"yes",实际上此处">"并非算术比较中的大于号,而为重定向输出,通过ls命令即可查看出当前目录下多出一个文件名为2的文件
[root@mghuee~chunlanyy testdir]# ll -l total 0 -rw-r--r--. 1 root root 0 Jun 17 20:53 2
有人也会试想通过转义符进行数值比较
[root@mghuee~chunlanyy testdir]# [ 1 \> 2 ] && echo yes || echo no no
通过此比较符合我们的预期,但是"\>"在此并非进行我们日常中理解的大小数值比较
[root@mghuee~chunlanyy testdir]# [ a \> 2 ] && echo yes || echo no yes
此处比较是进行ASCII码值大小比较,同时此处只能支持单字符的大小比较
[root@mghuee~chunlanyy testdir]# [ 5 \> 20 ] && echo yes || echo no yes
2."[]"支持的逻辑运算符为-a、-o、!,此处-a、-o相当于是test命令的一个参数,不能使用&&、||
[root@mghuee~chunlanyy testdir]# [ 1 -lt 2 && 1 -lt 3 ] && echo yes || echo no -bash: [: missing `]' no
&&和||是强逻辑运算,会对命令进行拆分,在上述举例中已经拆分成"[ 1 -lt 2 " && "1 -lt 3 ]",故会显示没有配对的"["
在逻辑运算时,[ expre1 -a expre 2 ]与[[ expre1 && expre2 ]]并非完全一致,&&会进行逻辑短路操作,而-a并不会
[root@mghuee~chunlanyy testdir]# [ 1 -gt 3 -a 2 > test1.txt ] && echo yes || echo no no [root@mghuee~chunlanyy testdir]# ls test1.txt
并未进行逻辑短路操作,执行所有判断,即使"1 -gt 3"已经错误,依旧执行后续操作,虽然结果为no,但是还会生成test1.txt文件
[root@mghuee~chunlanyy testdir]# [[ 1 -gt 3 && 2 > test2.txt ]] && echo yes || echo no no [root@mghuee~chunlanyy testdir]# ls test1.txt
进行逻辑运算操作,在与命令中,"1 -gt 3"为false即不作后续判断,故无test2.txt的出现
3."[]"是命令,故在其中的变量引用或常量,需使用双引号或者单引号括起来,因为会出现单词分割(Word-Splitting)现象
[root@mghuee~chunlanyy testdir]# I=" 29";[ -f $I ] && echo yes || echo no no [root@mghuee~chunlanyy testdir]# I=" 29";[ -f "$I" ] && echo yes || echo no yes [root@mghuee~chunlanyy testdir]# ll -a total 8 drwxr-xr-x. 2 root root 4096 Jun 17 23:20 . drwxrwxrwt. 6 root root 4096 Jun 17 20:53 .. -rw-r--r--. 1 root root 0 Jun 17 23:17 29
在当前目录中存在一个文件名为" 29"的文件(29前面有空格),在[]中未对引用变量加入双引号时,[]内部执行判断为-f "29",因为当前目录中不存在文件名的"29"的文件,显示结果是No
四.(())
支持四则运算,等同于let功能
[root@mghuee~chunlanyy testdir]# let I=2+4;echo $I 6 [root@mghuee~chunlanyy testdir]# I=$((2+4));echo $I 6
五.[[]]
"[["也是一个keyword,用于比较判断,基本支持"[]"中所有的判断比较符。
[root@mghuee~chunlanyy testdir]# type [[
[[ is a shell keyword
"[["和"["的不同点:
1.逻辑运算符不一致,"[[]]"为"&&"、"||","[]"为"-a"、"-o"。"[[]]"支持逻辑短路,而"[]"不支持
2."[[]]"支持正则表达式的匹配。
3."[[]]"为一个keyword,同括号与表达式中间必须要有空格进行隔离
4."[[]]"中使用比较符时不能转义,同时不会出现Word-Splitting
[root@mghuee~chunlanyy testdir]# I=" 29";[[ -f $I ]] && echo yes || echo no yes [root@mghuee~chunlanyy testdir]# ll -a total 8 drwxr-xr-x. 2 root root 4096 Jun 17 23:20 . drwxrwxrwt. 6 root root 4096 Jun 17 20:53 .. -rw-r--r--. 1 root root 0 Jun 17 23:17 29
与上述"[]"的结果进行比较,很显然,在"[[]]"中并不会出现Word-Splitting
[root@mghuee~chunlanyy testdir]# [[ a > 1 ]] && echo yes || echo no yes [root@mghuee~chunlanyy testdir]# [[ a \> 1 ]] && echo yes || echo no -bash: conditional binary operator expected -bash: syntax error near `\>'
可以看出,使用转义后出现error
5.[[]]中"=="与"=~"的使用
引用《advanced bash-scripting guide》对两者的解释
== String comparison operator <==字符串比较
=~ Regular Expression match operator <==正则表达式匹配
但是在此也并非代表"=="在"[[]]"中不能实现正则表达式模式的匹配,也能实现部分的匹配
[root@mghuee~chunlanyy tmp]# [[ abc == a*b***c ]] && echo yes || echo no yes
但只能实现字符之间的匹配
[root@mghuee~chunlanyy tmp]# [[ `cat /etc/passwd` == ^root ]] && echo yes || echo no no [root@mghuee~chunlanyy tmp]# [[ `cat /etc/passwd` =~ ^root ]] && echo yes || echo no yes
而"=~"则能完整的按正则表达式去匹配
注意:在正则表达式模式匹配中,右边的匹配模式不能使用双引号,在bash 3.2版本之后已经明确说明在使用正则表达式时所匹配的模式不能加上双引号
[root@mghuee~chunlanyy tmp]# [[ `cat /tmp/123` =~ "^root" ]] && echo yes || echo no yes [root@mghuee~chunlanyy tmp]# cat 123 "^root" [root@mghuee~chunlanyy tmp]# [[ `cat /tmp/123` =~ ^root ]] && echo yes || echo no no [root@mghuee~chunlanyy tmp]# cat 123 "^root"
因为在"[[]]"不会出现Word-Splitting,加入后反而让上匹配模式变成了“"^root"”
小结:
- 进行算术运算时使用"let命令",或者"$(())""
- 进行文件存在或者字符是否为空时使用"[]"
- 逻辑运算符"&&"、"||"要在"[[]]"中使用,"-a"、"-o"在"[]"中使用
- 在"[[]]"中使用正则表达式进行匹配的时候所匹配模式不能使用双引号
- 数值大小比较时在"[]"中进行比较,要使用"-lt"、"-gt"这样的比较符,不能使用"<"、">"
- "[[]]"与"[]"两边最好都保持空格,"[[]]"是两边一定要有空格进行隔离
- 以上均为习惯使用方式,并非说明其它组合模式一定不可使用,但可避免出现一些让人费解的错误