linux——shell脚本中关于$和特殊变量的使用细节

前言

原先刚学shell脚本的时候,经常会混淆$(( ))${}$()这些语法的使用,刚好最近有空,就来做一下相关的总结,方便后面查询。

一、和$相关的语法

(一) 单纯的$变量名

当我们定义完一个变量后(或者是已知环境变量中存在的变量名),那我们就可以通过在变量名前面加$的方式来引用这些变量
下面我们用一个小案例来演示一下

#!/bin/bash
field1=Hello
field2=World

echo $field1
echo $field2

执行结果为:

Hello
World

我们可以看到,我们定义的变量是可以正常打印到控制台的

使用$变量需要注意的细节
  • 变量名后面不要直接加其他字符串,防止识别错误。我们可以看看下面这个例子:
    我们定义了变量field1的值为Hello,希望输出HelloWorld。我们尝试直接在$field1后面加上World字符串,看一下输出的结果
#!/bin/bash
field1=Hello

echo '------------'
echo $field1World
echo '------------'

可以发现,打印出来的结果是空。这是因为字符串和变量名中间没有空格的话,脚本在执行的过程中会把field1World看成是一整个的变量名,而这个变量名我们没有定义,所以自然是找不到结果的。

------------

------------
(二)${变量名}

${变量名}$变量名的效果差不多,作用是引用我们定义的变量(包括环境变量),但${变量名}可以显式地标识我们希望引用的具体的变量,我们可以通过上文的案例来进行演示:

#!/bin/bash
field1=Hello

echo '------------'
echo ${field1}World
echo '------------'

执行结果为:

------------
HelloWorld
------------

我们可以发现,即使字符串World和变量之间不需要空格,脚本执行的时候也可以正常引用变量。

${}的进阶使用

${}的用法可不仅仅只是引用一下变量,它还具备着比较强大的文本处理功能,需要注意的是经过处理后得到的文本并不会改动到原有变量的值。

(1)字符串索引截取

当批量操作文本时,如果文本的长度和格式十分统一,那么适合用这种方式来对旧文本进行裁剪得到新文本

语法 解释
${str:offest} 从下标offset(含)开始截取到末尾的子串,下标索引从0开始,闭区间取值
${str:offest:length} 从下表offset(含)开始向后截取长度为length的子串,长度超出不报错
${str:offest:index} 此处的index为负数,最后一个字符为-1,依次往前为-2,-3。此表达式截取offset(含)到index(不含)之间的子串,当index表示的位置在offset左边时会报错
image.png
(2)字符串匹配裁剪

当批量操作文本时,如果文本的长度并不统一,但有相同的前缀或者后缀时,就比较适合用这种方式来进行操作

语法 解释
${str#var} 裁剪从左匹配到的第一个文本,可以使用*进行通配符匹配
${str##var} 裁剪从左匹配到的最后一个文本,可以使用*进行通配符匹配
${str%var} 裁剪从左匹配到的第一个文本,可以使用*进行通配符匹配
${str%%var} 裁剪从左匹配到的最后一个文本,可以使用*进行通配符匹配
image.png

上面涉及到的特殊符号主要是#%,我们可以这样来记
#号和%号在键盘上的位置分别位于$号的左右位置,所以前者用来表示匹配到的左边的文本,后者表示匹配到的右边的文本。
②单个符号表示最小匹配,两个符号表示最大匹配

(3)字符串替换
语法 解释
${str/old/new} 替换掉从左匹配到的第一个文本
${str//old/new} 替换到匹配到的所有文本
image.png
(4)设置默认值
语法 解释
${str-defaultVal} 若变量不存在,则给定默认值
${str:-defaultVal} 若变量不存在或者为空,则给定默认值
设置默认值
(5)返回字符串数量

${#str}:返回字符串的字符数


image.png
(三)$( 脚本 )

在执行一条命令时,shell会将$()中的语句当做命令执行一遍,再将结果加入到原命令中重新执行。简单概括的话,$( 脚本 )可以让我们获取到命令执行的结果值。
下面我们来用一个小案例来演示一下,结合echo命令来输出当前日期

#!/bin/bash

echo '------------'
echo "current date is $(date +%Y-%m-%d)"
echo '------------'

执行结果如下:

------------
current date is 2023-06-15
------------

我们可以看到,脚本在执行的过程中,确实是先把$()的内容执行后,再加入到了最终的文本中。需要注意的是,输出echo的内容的时候,不可以使用'',单引号里面的内容会被当做普通文本来处理。

PS:这里稍微拓展一下,`脚本` 和$(脚本) 这两个语法实现的效果是一致的,所以有时候我们会看到有些人是用`脚本`的方式来获取脚本的执行结果的。

(四)$(( 运算式 ))$[]运算式

这两个语法主要是用来帮助我们做一些计算的。我们知道在shell脚本中,我们定义的数字默认都会被识别为字符串。所以涉及到数值运算的话,我们可以借助$(( 运算式 )) 来帮助我们完成一些计算。
下面我们用一个案例来简单演示一下:

#!/bin/bash

result1=$((1+2+3))
result2=1+2+3
result3=$[1+2+3]

echo '------------'
echo "1+2+3=${result1}"
echo "1+2+3=${result2}"
echo "1+2+3=${result3}"
echo '------------'

从结果中我们可以看到,使用直接用1+2+3是得不到计算结果的,使用$(())或者$[]就可以正常拿到计算结果了。

------------
1+2+3=6
1+2+3=1+2+3
1+2+3=6
------------
(五)和$有关的特殊变量

在shell脚本中,除了我们自定义的变量以及环境变量外,shell中还存在着一些自带的特殊变量来供我们使用。这些特殊变量在我们跑一些比较复杂的脚本的时候,给予很大的帮助。

变量 含义
$0 当前脚本的文件名
$n (n>1) 传递给脚本或函数的参数。n是一个数字,表示第几个参数。例如,第一个参数是1,第二个参数是2。若n大于10,则语法为${n}
$# 传递给脚本或函数的参数个数
$* 传递给脚本或者函数的所有参数
$@ 传递给脚本或函数的所有参数。当被双引号""包含时,$@@*稍有不同
$? 上个命令的退出状态,或函数的返回值
$$ 当前Shell进程ID。对于Shell脚本,就是这些脚本所在进程ID

下面我们用一个简单的案例来演示一下,脚本内容如下:

#!/bin/bash

echo "====================="
echo "当前脚本文件名$0= $0"
echo "第一个参数$1= $1"
echo "第二个参数$2= $2"
echo "所有参数个数$#= $#"
echo "所有参数$*= $*"
echo "所有参数$@= $@"
echo "当前进程ID$$= $$"
echo "====================="

脚本执行结果如下,我们可以看到基本上相关的数据都正确打印出来了。

[root@10-60-159-92 testssh]# sh test.sh aa bb cc
=====================
当前脚本文件名test.sh= test.sh
第一个参数aa= aa
第二个参数bb= bb
所有参数个数3= 3
所有参数aa bb cc= aa bb cc
所有参数aa bb cc= aa bb cc
当前进程ID1432997= 1432997
=====================
关于$*$@的区别

在上一个案例中,我们会发现$*$@打印出来的结果是一样的,那实际上这两个特殊变量有存在什么区别吗?如果这两个特殊变量没有被""包含的时候,其实是没有区分的。但当他们被""包含时,区别就显示出来了:$*把所有的参数看做一个整体,而$@把每个参数区分对待。具体的区别我们可以用一个案例来演示一下:

#!/bin/bash

echo "====================="
echo "print all params by \"\$*\""
for var in "$*"
do
    echo $var
done
echo "====================="

echo "====================="
echo "print all params by \"\$@\""
for var in "$@"
do
    echo $var
done
echo "====================="

执行结果如下,我们可以发现,对于$@而言,它会自动将所有的变量当做一个个元素单独去处理。

[root@10-60-159-92 testssh]# sh test.sh aa bb cc
=====================
print all params by "$*"
aa bb cc
=====================
=====================
print all params by "$@"
aa
bb
cc
=====================

对于特殊变量的使用,需要注意我们反而不能使用例如:${0}或者${$0}来获取当前脚本的文件名,因为0或者$0并不是我们定义的变量名,使用${}反而会识别不出来。所以对于特殊变量,我们正常使用就行。同时,平时我们写脚本的时候,相关的变量名也尽量不要和这些特殊变量重复,避免脚本执行错误。

小结

个人觉得相关的语法还是属于用多了就记住了,如果记不住的话,有个地方可以快速回顾一下,那我们写shell脚本的时候就问题不大了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容