Shell编程中$(())与$()还有${}的区别

先来看$()

在bash shell中, $()与``(反引号)都是用来做命令替换(command substitution)的。 例如

1
$ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)

在操作上, 用$()或``都无所谓,但在使用时有一些需要注意的地方。

反引号在多命令复合的时候需要加额外的转义,

即 command1 `command2 \`command3\` `

要不然换成$()就没有问题了

1
command1 $(commmand2 $(command3))

做多少层的替换都没有问题

$

接下来,再让我们看看${}吧…它其实就是用来做 变量替换用的啦。 一般情况下,$var与${var}并没有啥不一样。 但是用${}会比较精准的界定变量名称的范围,比方说:

1
2
$ A=B
$ echo $AB

原本是打算先将$A的结果替换出来, 然后在其后补一个字母B; 但命令行上, 真正的结果却是替换变量名称为AB的值出来… 若使用${}就没有问题了:

1
2
$ A=B
$ echo ${A}B  #输出就是BB

1
2
3
4
5
6
7
#!/bin/bash                                                                                            

A=B
echo $A
echo $AB
echo $(A)B
echo ${A}b

输出如下:

B

./test.sh: line 6: A: command not found
B
Bb

此外${}还有更强大的功能

可以用${}分别替换获得不同的值

首先定义:

1
file=/dir1/dir2/dir3/my.file.txt

shell 字符串的非贪婪(最小匹配)左删除

1
${file#*/}  #其值为:dir1/dir2/dir3/my.file.txt

拿掉第一个/及其左边的字符串,其结果为: dir1/dir2/dir3/my.file.txt

1
${file#*.}  #其值为:file.txt

拿掉第一个.及其左边的字符串,其结果为: file.txt

shell 字符串的贪婪(最大匹配)左删除

1
${file##*/}  #其值为:my.file.txt

拿掉最后一个/及其左边的字符串,其结果为: my.file.txt

1
${file##*.}  #其值为:.txt

拿掉最后一个.及其左边的字符串,其结果为: txt

shell 字符串的非贪婪(最小匹配)右删除

1
${file%/*}  #其值为:/dir1/dir2/dir3

拿掉最后一个/及其右边的字符串,其结果为: /dir1/dir2/dir3

1
${file%.*}  #其值为:/dir1/dir2/dir3/my.file

拿掉最后一个.及其右边的字符串,其结果为: /dir1/dir2/dir3/my.file

shell 字符串的贪婪(最大匹配)右删除

1
${file%%/*}  #其值为:其值为空。

拿掉第一个/及其右边的字符串,其结果为: 空串。

1
${file%%.*}  #其值为:/dir1/dir2/dir3/my。

拿掉第一个.及其右边的字符串,其结果为: /dir1/dir2/dir3/my

shell 字符串取子串

1
2
${file:0:5} #提取最左边的5个字符:/dir1
${file:5:5} #提取第5个字符及其右边的5个字符:/dir2

shell 字符串取子串的格式:${s:pos:length}, 取字符串 s 的子串:从 pos 位置开始的字符(包括该字符)的长度为 length 的的子串; 其中pos为子串的首字符,在 s 中位置; length为子串的长度;

Note: 字符串中字符的起始编号为0

shell 字符串变量值的替换

1
2
${file/dir/path}  #将第一个dir替换为path:/path1/dir2/dir3/my.file.txt
${file//dir/path} #将全部的dir替换为path:/path1/path2/path3/my.file.txt

shell 字符串变量值的替换格式:

首次替换: ${s/src_pattern/dst_pattern} 将字符串s中的第一个src_pattern替换为dst_pattern。
全部替换: ${s//src_pattern/dst_pattern} 将字符串s中的所有出现的src_pattern替换为dst_pattern.

计算 shell 字符串变量的长度

1
${#file}  #其值为27, 因为/dir1/dir2/dir3/my.file.txt刚好为27个字符。

bash 数组(array)的处理方法

一般而言, A="a b c def" 这样的变量只是将$A替换为一个字符串, 但是改为 A=(a b c def), 则是将$A定义为数组….

数组替换方法可参考如下方法

1
2
${A[@]} #方法一
${A[*]} #方法二

以上两种方法均可以得到:a b c def, 即数组的全部元素。

访问数组的成员

${A[0]}其中,${A[0]}可得到a, 即数组A的第一个元素, 而 ${A[1]}则为数组A的第二元素,依次类推。

数组的 length

1
2
${#A[@]} #方法一
${#A[*]} #方法二

以上两种方法均可以得到数组的长度: 4, 即数组的所有元素的个数。

回忆一下,针对字符串的长度计算,使用${#str_var}; 我们同样可以将该方法应用于数组的成员:

${#A[0]}其中,${#A[0]}可以得到:1,即数组A的第一个元素(a)的长度; 同理,${#A[3]}可以得到: 3, 即数组A的第4个元素(def)的长度。

数组元素的重新赋值

A[3]=xyz

$(())作用

$(())是用来作整数运算的。

在bash中, $(())的整数运算符号大致有这些:

  • +- * / #分别为"加、减、乘、除"。
  • % #余数运算,(模数运算)
  • & | ^ ! #分别为"AND、OR、XOR、NOT"运算。

例如:

1
2
3
4
5
6
7
$ a=5; b=7; c=2;
$ echo $(( a + b * c ))
19
$ echo $(( (a + b)/c ))
6
$ echo $(( (a * b) % c ))
1

在$(())中的变量名称, 可以在其前面加 $符号来替换, 也可以不用,如: $(( $a + $b * $c )) 也可以得到19的结果。

此外,$(())还可作不同进制(如二进制、八进制、十六进制)的运算, 只是输出结果均为十进制的。

echo $(( 16#2a )) 输出结果为:42,(16进制的2a) 以一个实用的例子来看看吧 : 假如当前的umask是022,那么新建文件的权限即为:

1
2
3
$ umask 022
$ echo "obase=8; $(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
644

事实上,单纯用(())也可以重定义变量值,或作 testing:

1
2
3
a=5; ((a++)) #可将$a 重定义为6
a=5; ((a--)) #可将$a 重定义为4
a=5; b=7; ((a< b)) #会得到0 (true)返回值。

Note:使用(())作整数测试时, 请不要跟[]的整数测试搞混乱了。