()
连接多个命令组,用;
隔开,最后一个命令后;
可加可不加。括号中的命令会新开一个子shell顺序执行,所以里面的操作对括号外主进程无关。
a=0
(a=1;b=2;echo $a) # 子进程内输出a为1
echo $a # 主进程,输出a为0
格式为 $(command)
,相当于 `command`
。shell遇到此格式,会先执行 command
命令,得到标准输出,再将此输出放回到原来命令。
SCRIPTPATH=$(dirname $0) # sh脚本的相对目录
Shell 数组用括号来表示,元素用"空格"符号分割开。
array=(a b c)
echo ${array[1]} # 输出b
(())
表达式 $((exp))
,其中exp
只要符合C语言规则的运算符即可,包括加减乘除、+=
、<=
、>=
等。
进行不同进位(如二进制、八进制、十六进制)运算时,输出结果会全都自动转化成十进制。
echo $((2*2+(1-2))) # 输出3
a=1
echo $((a++)) # 输出2,且从此之后a=2
echo $((2#10+4)) # 输出6,在10前面的2#表示这是个二进制
echo $((a<2)) # 输出1。如果为假,则输出0。
在((exp))
中可以对变量进行定义或重新赋值,且之后脚本全部有效(不是像()
那样只在子进程有效)。
a=1
((a=2))
echo $a # 输出2
在((exp))
中可以进行算术比较(不能进行字符串比较),双括号中的变量可以省略$
符号前缀,当然也可以带着。
a=1
((a==1)) && echo "true" # 输出 true
(($a==1)) && echo "true" # 输出 true
[]
[]
本质上是 test
语句,[
是调用test
的命令标识,]
是关闭条件判断,如:
a="abc"
if [ -n "$a" ]; then echo "true"; fi # 输出true,注意[]内-n或者-z时字符串两边必须加上双引号
if test -n "$a"; then echo "true"; fi # 输出true,注意test内-n或者-z时字符串两边必须加上双引号
用作正则表达式的一部分,描述一个匹配的字符范围,如删除一个字符串中的所有数字:
echo "ap1p23le" | tr -d "[0-9]" # 输出 apple
echo [a-b].txt # 输出:a.txt b.txt,前提是两个文件都存在。
# 但是如果当前路径只存在a.txt或b.txt,则只会输出存在的文件。
# 如果两个文件均不存在,则输出固定字符串:[a-b].txt
在一个array
后,作为引用数组元素的下标,如
a=(1 2 3)
echo ${a[0]} # 输出:1
echo ${a[*]} # 输出:1 2 3
[[]]
使用[[]]
条件判断结构,与[]
有以下区别:
[]
和test
是命令,而[[]]
是关键字,所以重定向等字符在[]
中会被认为成重定向,而在[[]]
中是比较符号的意思。&&
、||
、<
和>
操作符能够正常存在于[[]]
条件判断结构中,但是如果出现在[]
结构中的话,会报错。比如可以直接使用if [[ $a != 1 && $a != 2 ]]
, 如果不使用双括号, 则为if [ $a != 1] && [ $a != 2 ]
或者if [ $a != 1 -a $a != 2 ]
[[]]
支持字符串的模式匹配,[]
不支持。使用=
或==
进行字符串比较,等号右边的可以作为一个模式,比如[[ "hello" == hell? ]]
为真。模式匹配不能加引号,否则会作为固定字符串,如[[ "hello" == "hell?" ]]
为假。[[]]
支持算术扩展,而[]
不支持。如if [[ 1+2 -eq 3 ]]
,而if [ 1+2 -eq 3 ]
则会报错。本质上,bash把[[]]
中的表达式看作一个单独的元素,并返回一个退出状态码。
比如:
[[ 1 -gt 0 ]]
echo $? # 输出0
[[ 1 -gt 2 ]]
echo $? # 输出1
可以看到,如果[[]]
内的表达式为真,则返回退出码0
,这也是其他所有命令执行正常时所返回的退出码。
因此,如果用[[]]
作为if
的判断条件,道理是和判断一个命令或函数是否顺利执行是一样的。
{}
{}
可以作为通配符进行扩展,有两种用法:
echo {a,b}.txt
输出a.txt b.txt
。与echo [a-b].txt
区别在于,后者只输出存在的文件名,而前者不管是不是存在此文件,永远都会输出a.txt b.txt
,因为{}
的字符扩展和是不是文件名没有关系。echo {a..c}.txt
输出a.txt b.txt c.txt
。这两种扩展也可以用于循环:
$ for i in {0,2};do echo $i;done
0
2
$ for i in {0..2};do echo $i;done
0
1
2
又被称为内部组,格式为{ cmd1;cmd2;}
,这个结构事实上创建了一个匿名函数 。
{}
与()
都可以执行一连串命令,区别如下:
()
会新开一个子进程,括号内命令与括号外无关。{}
内的命令不会新开一个子进程运行,即脚本余下部分仍可使用括号内变量。;
隔开,但()
最后一个命令的分号可有可无,但{}
最后一个也必须有分号。{}
的第一个命令和左括号{
之间必须要有一个空格(右括号}
无此要求),而()
两个括号的空格均可有可无。有4种特殊的替换结构:${var:-string},${var:+string},${var:=string},${var:?string}
(1)${var:-string}
表示若变量var
不为空,则等于var
的值,否则等于string
的值。
比如:
a=''
b="bbb"
echo ${a:-foo} # 输出:foo
echo ${a:-"foo"} # 输出:foo
echo ${a:-$b} # 输出:bbb
(2)${var:=string}
的替换规则与${var:-string}
相同,不过多了一步操作:若var
为空时,还会把string
的值赋给var
。
比如:
a=''
b="bbb"
echo ${a:=$b} # 输出:bbb
echo $a # 输出:bbb
(3)${var:+string}
的替换规则和上面的相反,表示若变量var
不为空,则等于string
的值,否则等于var
的值(即空值)。
比如:
a=""
b="bbb"
echo ${a:+"foo"} # 输出为空
echo ${b:+"foo"} # 输出:foo
(4)${var:?string}
表示若变量var
不为空,则等于var
的值,否则把string
输出到标准错误中,并从脚本中退出。
我们可利用此特性来检查是否设置了变量的值,比如一个脚本test.sh
内容如下:
#!/bin/bash
a=""
b="bbb"
echo ${a:?"warn:string is null"}
echo ${b:?"warn:string is null"}
echo "done"
然后在命令行中执行命令:
$ sh test.sh > out.log 2> err.log # 标准输出至out.log,标准错误输出至err.log
$ cat out.log
aaa
$ cat err.log
test.sh: line 5: b: warn:string is null
可以看到,out.log
中没有done
,因此脚本执行到Line 5
的错误就提前退出了。
有4种模式:${var%pattern},${var%%pattern},${var#pattern},${var##pattern}
(1)${var%pattern}
表示看var
是否以模式pattern
结尾,如果是,就把var
中的内容去掉右边最短的匹配模式。
(2)${var%%pattern}
表示看var
是否以模式pattern
结尾,如果是,就把var
中的内容去掉右边最长的匹配模式。
(3)${var#pattern}
表示看var
是否以模式pattern
开始,如果是,就把var
中的内容去掉左边最短的匹配模式。
(4)${var##pattern}
表示看var
是否以模式pattern
开始,如果是,就把var
中的内容去掉左边最长的匹配模式。
比如:
a="apple"
echo ${a%"le"} # 去掉右边固定"le",输出:app
echo ${a%p*} # 去掉右边最短匹配,输出:ap
echo ${a%%p*} # 去掉右边最长匹配,输出:a
echo ${a#*p} # 去掉左边最短匹配,输出:ple
echo ${a##*p} # 去掉左边最长匹配,输出:le
有2种模式:${var:num},${var:num1:num2}
(1)${var:num}
表示提取var
中第num
个字符到末尾的所有字符。若num
为正数,从左边0处开始;若num
为负数,从右边开始提取字串,但必须使用在冒号后面加空格或一个数字或整个num
加上括号,如${var: -2}
、${var:1-3}
或${var:(-2)}
。
(2)${var:num1:num2}
表示从var
中第num1
个位置开始提取长度为num2
的子串。num1
从0开始。
比如:
a="apple"
echo ${a:2} # 去掉前2个,输出:ple
echo ${a: -2} # 保留后2个,输出:le
echo ${a:1:3} # 保留从1开始的3个字符,输出:ppl