💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
##shell十三问之8: $(())与$()还有${}差在哪? -------------------------------------------- 我们上一章介绍了()与{}的不同, 这次让我们扩展一下,看看更多的变化: $()与${}又是啥玩意儿呢? 在bash shell中, `$()`与\`\`(反引号)都是用来做 `命令替换`(command substitution)的。 所谓的`命令替换`与我们第五章学过的变量替换差不多, 都是用来`重组命令行`: 完成 \`\` 或者`$()`里面的 命令,将其结果替换出来, 再重组命令行。 例如: ```shell $ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d) ``` 如此便可方便得到上一个星期天的日期了...^_^ 在操作上, 用$()或\`\`都无所谓, 只是我个人比较喜欢用$(),理由是: 1. \`\`(反引号)很容易与''(单引号)搞混乱,尤其对初学者来说。 有时在一些奇怪的字形显示中,两种符号是一模一样的(只取两点)。 当然了有经验的朋友还是一眼就能分辨两者。只是,若能更好的避免混乱, 又何乐而不为呢? ^_^ 2. 在多次的复合替换中, \`\`需要额外的转义(escape, \)处理,而$()则比较直观。 例如,一个错误的使用的例子: ```shell command1 `command2 `command3` ` ``` 原来的本意是要在command2 \`command3\` , 先将command3替换出来给command2处理, 然后再将command2的处理结果,给command1来处理。 然而真正的结果在命令行中却是分成了\`command2\`与 \`\`。 正确的输入应该如下: ```shell command1 `command2 \`command3\` ` ``` 要不然换成$()就没有问题了: ```shell command1 $(commmand2 $(command3)) ``` 只要你喜欢,做多少层的替换都没有问题~~~^_^ 不过,$()并不是没有弊端的... 首先,\`\`基本上可用在所有的unix shell中使用, 若写成 shell script,其移植性比较高。 而$()并不是每一种shell都能使用,我只能说, 若你用bash2的话,肯定没问题... ^_^ 接下来,再让我们看看${}吧...它其实就是用来做 变量替换用的啦。 一般情况下,$var与${var}并没有啥不一样。 但是用${}会比较精准的界定变量名称的范围, 比方说: ```shell $ A=B $ echo $AB ``` 原本是打算先将$A的结果替换出来, 然后在其后补一个字母B; 但命令行上, 真正的结果却是替换变量名称为AB的值出来... 若使用${}就没有问题了: ```shell $ A=B $ echo ${A}B $ BB ``` 不过,假如你只看到`${}`只能用来界定变量名称的话, 那你就实在太小看bash了。 为了完整起见,我这里再用一些例子加以说明`${}`的一些 特异功能: 假设我们定义了一个变量file为: ```shell file=/dir1/dir2/dir3/my.file.txt ``` 我们可以用`${}`分别替换获得不同的值: ####1. shell字符串的非贪婪(最小匹配)左删除 ----------- ```shell ${file#*/} #其值为:dir1/dir2/dir3/my.file.txt ``` 拿掉第一个`/`及其左边的字符串,其结果为: `dir1/dir2/dir3/my.file.txt` 。 ```shell ${file#*.} #其值为:file.txt ``` 拿掉第一个`.`及其左边的字符串,其结果为: `file.txt` 。 ####2. shell字符串的贪婪(最大匹配)左删除: ------ ```shell ${file##*/} #其值为:my.file.txt ``` 拿掉最后一个`/`及其左边的字符串,其结果为: `my.file.txt` ```shell ${file##*.} #其值为:txt ``` 拿掉最后一个`.`及其左边的字符串,其结果为: `txt` ####3. shell字符串的非贪婪(最小匹配)右删除: ---------- ```shell ${file%/*} #其值为:/dir1/dir2/dir3 ``` 拿掉最后一个`/`及其右边的字符串,其结果为: `/dir1/dir2/dir3`。 ```shell ${file%.*} #其值为:/dir1/dir2/dir3/my.file ``` 拿掉最后一个`.`及其右边的字符串,其结果为: `/dir1/dir2/dir3/my.file`。 ####4. shell字符串的贪婪(最大匹配)右删除: ----- ```shell ${file%%/*} #其值为:其值为空。 ``` 拿掉第一个`/`及其右边的字符串,其结果为: 空串。 ```shell ${file%%.*} #其值为:/dir1/dir2/dir3/my。 ``` 拿掉第一个`.`及其右边的字符串,其结果为: /dir1/dir2/dir3/my。 > **Tips:** > 记忆方法: > `#`是去掉左边(在键盘上`#`在`$`的左边); > `%`是去掉右边(在键盘上`%`在`$`的右边); > 单个符号是最小匹配; > 两个符号是最大匹配; ####5. shell字符串取子串: ---- ```shell ${file:0:5} #提取最左边的5个字符:/dir1 ${file:5:5} #提取第5个字符及其右边的5个字符:/dir2 ``` shell字符串取子串的格式:`${s:pos:length}`, 取字符串s的子串:从pos位置开始的字符(包括该字符)的长度为length的的子串; 其中pos为子串的首字符,在s中位置; length为子串的长度; > **Note:** 字符串中字符的起始编号为0. ####6. shell字符串变量值的替换: ----- ```shell ${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. ####7. ${}还可针对变量的不同状态(没设定、空值、非空值)进行赋值: ------ - `${file-my.file.txt}` #如果file没有设定,则使用 使用my.file.txt作为返回值, 否则返回${file};(空值及非空值时,不作处理。); - `${file:-my.file.txt}` #如果file没有设定或者${file}为空值, 均使用my.file.txt作为其返回值,否则,返回${file}.(${file} 为非空值时,不作处理); - `${file+my.file.txt}` #如果file已设定(为空值或非空值), 则使用my.file.txt作为其返回值,否则不作处理。(未设定时,不作处理); - `${file:+my.file.txt}` #如果${file}为非空值, 则使用my.file.txt作为其返回值,否则,(未设定或者为空值时)不作处理。 - `${file=my.file.txt}` #如果file为设定,则将file赋值为my.file.txt,同时将${file}作为其返回值;否则,file已设定(为空值或非空值),则返回${file}。 - `${file:=my.file.txt}` #如果file未设定或者${file}为空值, 则my.file.txt作为其返回值, 同时,将${file}赋值为my.file.txt,否则,(非空值时)不作处理。 - `${file?my.file.txt}` #如果file没有设定,则将my.file.txt输出至STDERR, 否侧, 已设定(空值与非空值时),不作处理。 - `${file:?my.file.txt}` #若果file未设定或者为空值,则将my.file.txt输出至STDERR,否则, 非空值时,不作任何处理。 > **Tips:** > 以上的理解在于,你一定要分清楚,`unset`与`null`以及non-null这三种状态的赋值; >一般而言,与null有关,若不带`:`, null不受影响; > 若带 `:`, 则连null值也受影响。 ####8. 计算shell字符串变量的长度:`${#var}` -------------- ```shell ${#file} #其值为27, 因为/dir1/dir2/dir3/my.file.txt刚好为27个字符。 ``` ####9. bash数组(array)的处理方法 ------------------- 接下来,为大家介绍一下bash的数组(array)的处理方法。 一般而言, `A="a b c def"` 这样的变量只是将`$A`替换为一个字符串, 但是改为 `A=(a b c def)`, 则是将`$A`定义为数组.... #####1). 数组替换方法可参考如下方法: ```shell ${A[@]} #方法一 ${A[*]} #方法二 ``` 以上两种方法均可以得到:a b c def, 即数组的全部元素。 #####2). 访问数组的成员: ```shell ${A[0]} ``` 其中,`${A[0]}`可得到a, 即数组A的第一个元素, 而 `${A[1]}`则为数组A的第二元素,依次类推。 #####3). 数组的length: ```shell ${#A[@]} #方法一 ${#A[*]} #方法二 ``` 以上两种方法均可以得到数组的长度: 4, 即数组的所有元素的个数。 回忆一下,针对字符串的长度计算,使用`${#str_var}`; 我们同样可以将该方法应用于数组的成员: ```shell ${#A[0]} ``` 其中,`${#A[0]}`可以得到:1,即数组A的第一个元素(a)的长度; 同理,`${#A[3]}`可以得到: 3, 即数组A的第4个元素(def)的长度。 #####4). 数组元素的重新赋值: ```shell A[3]=xyz ``` 将数组A的第四个元素重新定义为xyz。 > **Tips:** > 诸如此类的... > 能够善用bash的$()与${}可以大大提高及 > 简化shell在变量上的处理能力哦~~~^_^ ####10. $(())作用: ---- 好了,最后为大家介绍`$(())`的用途吧: **`$(())`是用来作整数运算的**。 在bash中, `$(())`的整数运算符号大致有这些: - \+\- * / #分别为"加、减、乘、除"。 - % #余数运算,(模数运算) - & | ^ ! #分别为"AND、OR、XOR、NOT"运算。 例如: ```shell $ 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的结果。 此外,`$(())`还可作不同进制(如二进制、八进制、十六进制)的运算, 只是输出结果均为十进制的。 ```shell echo $(( 16#2a )) #输出结果为:42,(16进制的2a) ``` 以一个实用的例子来看看吧 : 假如当前的umask是022,那么新建文件的权限即为: ```shell $ umask 022 $ echo "obase=8; $(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc 644 ``` 事实上,单纯用`(())`也可以重定义变量值,或作testing: ```shell a=5; ((a++)) #可将$a 重定义为6 a=5; ((a--)) #可将$a 重定义为4 a=5; b=7; ((a< b)) #会得到0 (true)返回值。 ``` 常见的用于`(())`的测试符号有如下这些: |符号|符号名称| |----|--------| | < | 小于号 | | > | 大于号 | | <= | 小于或等于| | >= | 大于或等于| | == | 等于| | != | 不等于| > **Note:** > 使用`(())`作整数测试时, > 请不要跟`[]`的整数测试搞混乱了。 > 更多的测试,我们将于第10章为大家介绍。 怎样? 好玩吧... ^_^ okay,这次暂时说这么多... 上面的介绍,并没有详列每一种可用的状态, 更多的,就请读者参考手册文件(man)吧...