摘要:Shell 中的符号组合种类很多,比如 () (()) $() $(()) [] [[]] {} ${} 等等,有的做数值运算的修饰符,有的做关系运算符的修饰符,有的做变量引用,有的做命令替换,形状类似但用法却完全不同。

本文根据用途划分,将 shell 中容易混淆用法的符号做了归纳总结,希望读罢此文的同学能够准确说出这些符号的用法,或者在脑海里梳理成树状结构并自己画画思维导图,有助于于记忆和避免使用出错。

命令替换符

`command`  # 两个反引号
$(command) 

两者都有命令替换的作用,包裹在中间的 shell 命令将被执行,并将返回结果输出到标准输出里。

$ echo `pwd`
/Users/zither

$ echo $(pwd)
/Users/zither

变量替换符

${variable} # 也可省略 {} 写成 $variable,加花括号是为了帮助解释器识别变量的边界,也是一个好的编程习惯。

变量替换符即变量引用,它还有很多高级的用法:

# 对于字符串变量
# 提取
${string:position}        # 在 $string 中, 从位置 $position 开始提取子串
${string:position:length} # 在 $string 中, 从位置 $position 开始提取长度为 $length 的子串

# 去除首尾
${string#expression}      # 从变量 $string 的开头, 删除最短匹配 $expression 的子串
${string##expression}     # 从变量 $string 的开头, 删除最长匹配 $expression 的子串
${string%expression}      # 从变量 $string 的结尾, 删除最短匹配 $expression 的子串
${string%%expression}     # 从变量 $string 的结尾, 删除最长匹配 $expression 的子串

# 替换
${string/expression/replacement}    # 使用 $replacement, 来代替第一个匹配的 $expression
${string//expression/replacement}   # 使用 $replacement, 代替所有匹配的 $expression
${string/#expression/replacement}   # 如果 $string 的前缀匹配 $expression, 那么就用 $replacement 来代替匹配到的 $expression
${string/%expression/replacement}   # 如果 $string 的后缀匹配 $expression, 那么就用 $replacement 来代替匹配到的 $expression 

# 其他基础用法
$ str=luckycoding;echo ${#str}
11                                  # 使用 # 号获取字符串的长度

$ array=(www lucky coding com);echo ${#array} ${#array[*]} ${#array[*]}
3 4 4                               # 使用 # 号分别获取数组第一个元素长度、数组元素个数、数组元素个数

单引号和双引号

字符串定义可以使用单引号也可以使用双引号,两种引号都可以对字符串进行拼接,不同点在于单引号包裹的字符串中不能再单独使用单引号 (转义也不可以),且单引号内不能进行变量引用

$ s1="to";echo 'lucky $s1 coding';echo "lucky $s1 coding"
lucky $s1 coding
lucky to coding

数值运算符修饰符

1.使用修饰符 $[]、$(()) 进行数值运算。

$ echo $[ 2 + 2 ]
4

$ echo $((5+3))
8
# 注意这两个符号都不能进行符点运算

2.使用 let、(()) 进行数值运算赋值。let、(()) 两者支持一些高级的数值运算如类似 C 语言的 i++、i+=1 等等,但是只能用来赋值或比较,不能直接得出计算结果。

$ let i=1 i++;echo $i
2

$ i=1;((i+=1));echo $i
2
# 流程控制中 while、for 等可以通过(())符号进行 C 语言形式的循环写法

3.使用命令程序来完成数值运算,Linux/Unix 中有很多可以进行数值运算的命令,如 bc、expr、let、awk 等,其中 bc 、awk 支持浮点除法:

$ echo "scale=2; 5/3" | bc  # scale 变量控制 bc 输出的小数位数
1.66

$ echo | awk '{print 5/3}'  # 这里 "" 将作为 awk 的输入
1.66667

$ expr 2 + 2                # 使用 expr 注意空格格式 
4

本文主要介绍符号,关于使用命令进行数值运算不再深入,感兴趣的同学自行搜索了解。

关系运算符修饰符

有关系运算符参与的表达式,输出的结果一般为 ture 或 false。shell 中支持关系运算符的修饰符主要使用 test 、[ ]、(( ))、[[ ]],其中 test 、[ ] 两者的作用时一样的,注意在使用这两个修饰符时需要在关系运算符两边均需要空格。

# -eq 为判断整数数值是否相等
$ if test 1 -eq 1; then echo yes; fi    
yes

# -n 为判断字符串长度是否不为 0
$ $ if [ -n "LuckyCoding" ]; then echo yes; fi  
yes

下面在不同使用场景分别说明:

数值关系

数值关系运算符,==、!=、<、>

$ if [ 1.2 == 1.2 ]; then echo yes; fi
yes

$ if [ 1.2 \< 1.3 ]; then echo yes; fi    # 与重定向符冲突,注意转义
yes

以下关系运算符只支持整数数值

符号 作用 备注
-eq 等于 equals
-ne 不等于 not equals
-lt 小于 less then
-gt 大于 greater then
-le 小于等于 less than or equal to
-ge 大于等于 greater then or equal to
$ if [ 1.2 -lt 1.3 ]; then echo yes; fi
-bash: [: 1.2: 期待整数表达式

# 使用修饰符 (()) 来进行数值关系比较也只支持整数
$ if ((1<1.2)); then echo yes; fi
-bash: ((: 1<1.2: 语法错误: 无效的算术运算符 (错误符号是 ".2"

如需对符点型数值进行 -le -ge 等比较,则需要借助 expr 、bc、awk 来实现

$ expr 1.3 \<= 1.4; expr 1.3 \<= 1.3; expr 1.3 \<= 1.2
1
1
0

$ echo "1.3 <= 1.4" | bc; echo "1.3 <= 1.3" | bc; echo "1.3 <= 1.2" | bc
1
1
0

$ echo | awk '{print (1.2<=1.3)?1:0}'
1

字符串关系

字符串关系运算符 =、!=、-z、-n、$ ,仅用于字符串比较关系。

符号 作用
-z $String 判断字符串长度是否为 0
-n "$String" 判断字符串长度是否不为 0
$String 判断字符串是否为 null
# 关系运算符 == 在比较字符串变量时与 = 等价,
$ if [ lucky = lucky ]; then echo yes; fi
yes

$ if [ coding == coding ]; then echo yes; fi
yes

$ if [ -n "LuckyCoding" ]; then echo yes; fi
yes

布尔值关系

布尔值关系运算符 -a、-o、!

$ if [ 1 -eq 1 -a 1 -ne 2 ]; then echo yes; fi
yes

$ if [ ! 1 -eq 2 ]; then echo yes; fi
yes

文件关系

常用文件关系运算符如下 (Linux 下一切皆为文件):

符号 作用
-e 文件是否存在
-s 文件大小是否为 0
-b、-c、-d、-f 文件是否为 块设备文件、字符设备文件、目录文件、普通文件
-r、-w、-x 文件是否为 可读文件、可写文件、可执行文件

逻辑关系

逻辑运算符 &&、||, 逻辑运算符需要使用修饰符 [[ ]] 包围,且不能使用修饰符 [ ]。

符号 作用
&& 上一条语句 $? 为 0,&& 后的语句执行
丨丨 上一条语句 $? 不为 0,丨丨 后的语句执行
$ if [[ 1 -eq 1 && 2 -lt 3 ]]; then echo yes; else echo no; fi
yes

$ if [[ 1 -eq 1 && 2 -lt 1 ]]; then echo yes; else echo no; fi
no

$ if [[ 1 -eq 1 || 2 -lt 1 ]]; then echo yes; else echo no; fi
yes

$ if [[ 2 -eq 1 || 2 -lt 1 ]]; then echo yes; else echo no; fi
no

Tips: 在使用修饰符 [] 时,如果里面的比较参数为变量引用的话,最好在变量引用的同时加上双引号,[[]] 则可以不用加双引号,可以将 [[]] 理解为增强版的 []

# 在比较两个字符串长度为 0 的变量 var1 和 var2 时
$ var1="" var2=""; if [[ $var1 != $var2 ]]; then echo yes; else echo no; fi
no

# 判断2个变量是否不等,结果也返回 yes
$ var1="" var2=""; if [ $var1 != $var2 ]; then echo yes; else echo no; fi
yes   

$ var1="" var2=""; if [ "$var1" != "$var2" ]; then echo yes; else echo no; fi
no

运行时环境符号

() 除了用来定义数组,如 array=(1 2 3 4) 之外,还有一个作用,括号中的命令将会新开一个子 shell 顺序执行,括号中的变量不能够被脚本余下的部分使用。

$ var=1; (var=2; echo $var); echo $var
2
1
# Tips: 括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。

{} 有两种常见用法:
1.用于拓展字符串,此时 {} 不允许有空白,除非这个空白被引用或转义。

$ touch {lucky,coding}.sh; ls
coding.sh  lucky.sh

$ touch {a..f}.sh; ls
a.sh  b.sh  coding.sh  c.sh  d.sh  e.sh  f.sh  lucky.sh

$ echo var{1..7}
var1 var2 var3 var4 var5 var6 var7
# 使用 .. 对顺序做拓展

2.定义匿名函数,其实是使用 {} 定义 shell 中的一个匿名函数,这里不举例子,感兴趣的同学可以自己了解如何定义函数。
{} 也可以用于多行注释,原理相同,定义不调用即可。

原文地址:https://www.luckycoding.com/2019/03/14/shell-confusable-symbol-summarize


↙↙↙阅读原文可查看相关链接,并与作者交流