Shell脚本编程(二):shell变量

时间:2022-06-05 20:46:03

定义变量

定义变量时,变量名不加美元符号($,PHP语言中变量需要),如:

your_name="runoob.com"

注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
  • 中间不能有空格,可以使用下划线(_)。
  • 不能使用标点符号。
  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

有效的 Shell 变量名示例如下:

DEVICENAME
LD_LIBRARY_PATH
_var
var2

无效的变量命名:

?devicetype=
user name=runoob
status = 0
7isactive=1

除了显式地直接赋值,还可以用语句给变量赋值,如:

for file in `ls /etc`

for file in $(ls /etc)

以上语句将 /etc 下目录的文件名循环出来。

使用变量

使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

#!/bin/bash
devicetype='lift'
echo $devicetype

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。

已定义的变量,可以被重新定义,如:

#!/bin/bash
devicetype='lift'
echo $devicetype
devicetype='crane'
echo $devicetype

结果

lift
crane

这样写是合法的,但注意,第二次赋值的时候不能写$your_name="alibaba",使用变量的时候才加美元符($)。

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

下面的例子尝试更改只读变量,结果报错:

#!/bin/bash
pdfurl='http://www.xxxx.com/resource/pdfs/181101/6238258284.pdf'
echo $pdfurl
readonly pdfurl
pdfurl='http://www.xxxx.com/resource/pdfs/181111/6238258255.pdf'
echo $pdfurl

运行脚本,可以看到第二次赋值没有成功,并且报错,结果如下:

http://www.xxxx.com/resource/pdfs/181101/6238258284.pdf
http://www.xxxx.com/resource/pdfs/181101/6238258284.pdf
/tmp//main.sh: line : pdfurl: readonly variable

删除变量

使用 unset 命令可以删除变量。语法:

unset variable_name

变量被删除后不能再次使用。unset 命令不能删除只读变量。

实例

#!/bin/bash
# 定义两个变量并打印
pdfurl='http://www.xxxx.com/resource/pdfs/181101/6238258284.pdf'
imgurl='http://www.xxxx.com/resource/imgs/181101/6238258284.jpg'
echo $pdfurl
echo $imgurl
# 将pdfurl置为只读
readonly pdfurl
# 尝试删除这两个变量
unset pdfurl
unset imgurl
# 再次打印
echo $pdfurl
echo $imgurl

执行实例,可以看到第二次只打印了imgurl。

http://www.xxxx.com/resource/pdfs/181101/6238258284.pdf
http://www.xxxx.com/resource/imgs/181101/6238258284.jpg
http://www.xxxx.com/resource/pdfs/181101/6238258284.pdf /tmp//main.sh: line : unset: pdfurl: cannot unset: readonly variable

变量作用域

Shell变量的作用域(Scope),就是 Shell 变量的有效范围(可以使用的范围)。

在不同的作用域中,同名的变量不会相互干涉,就好像 A 班有个叫小明的同学,B 班也有个叫小明的同学,虽然他们都叫小明(对应于变量名),但是由于所在的班级(对应于作用域)不同,所以不会造成混乱。但是如果同一个班级中有两个叫小明的同学,就必须用类似于“大小明”、“小小明”这样的命名来区分他们。

Shell 变量的作用域可以分为三种:

  • 有的变量可以在当前 Shell 会话中使用,这叫做全局变量(global variable)
  • 有的变量只能在函数内部使用,这叫做局部变量(local variable)
  • 而有的变量还可以在其它 Shell 中使用,这叫做环境变量(environment variable)

1.全局变量

所谓全局变量,就是指变量在当前的整个 Shell 会话中都有效。每个 Shell 会话都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量。

想要实际演示全局变量在不同 Shell 会话中的互不相关性,可在图形界面下同时打开两个 Shell,或使用两个终端远程连接到服务器(SSH)。

首先打开一个 Shell 窗口,定义一个变量 a 并赋值为 1,然后打印,这时在同一个 Shell 窗口中是可正确打印变量 a 的值的。然后再打开一个新的 Shell 窗口,同样打印变量 a 的值,但结果却为空,如图 所示。

Shell脚本编程(二):shell变量

这说明全局变量 a 仅仅在定义它的第一个 Shell 中有效,对其它 Shell 没有影响。这很好理解,就像小王家和小徐家都有一部电视机(变量名相同),但是同一时刻小王家和小徐家的电视中播放的节目可以是不同的(变量值不同)。

需要强调的是,全局变量的作用范围是当前的 Shell 会话,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 会话,打开多个 Shell 窗口就创建了多个 Shell 会话,每个 Shell 会话都是独立的进程,拥有不同的进程 ID。在一个 Shell 会话中,可以执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。

例如,现在有两个 Shell 脚本文件,分别是 a.sh 和 b.sh。a.sh 的代码如下:

#!/bin/bash
echo $a
b=
b.sh 的代码如下:
#!/bin/bash

echo $b
打开一个 Shell 窗口,输入以下命令:
$ a=
$ . ./a.sh $ . b.sh $
从输出结果可以发现,在 Shell 会话中以命令行的形式定义的变量 a,在 a.sh 中有效;在 a.sh 中定义的变量 b,在 b.sh 中也有效。

2.局部变量

Shell 也支持自定义函数,但是 Shell 函数和 C/C++、Java 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。请看下面的代码:
#!/bin/bash
#定义函数
function func(){
a=
}
#调用函数
func
#输出函数内部的变量
echo $a
输出结果:

a 是在函数内部定义的,但是在函数外部也可以得到它的值,证明它的作用域是全局的,而不是仅限于函数内部。

要想变量的作用域仅限于函数内部,那么可以在定义时加上local命令,此时该变量就成了局部变量。请看下面的代码:
#!/bin/bash
#定义函数
function func(){
local a=
}
#调用函数
func
#输出函数内部的变量
echo $a
输出结果为空,表明变量 a 在函数外部无效,是一个局部变量。
Shell 变量的这个特性和 JavaScript 中的变量是类似的。在 JavaScript 函数内部定义的变量,默认也是全局变量,只有加上var关键字,它才会变成局部变量。

3.环境变量

全局变量只在当前 Shell 会话中有效,如果使用export命令将它导出,那么它就在所有的子 Shell 中也有效了,这称为“环境变量”。

环境变量被创建时所处的 Shell 被称为父 Shell,如果在父 Shell 中再创建一个 Shell,则该 Shell 被称作子 Shell。当子 Shell 产生时,它会继承父 Shell 的环境变量为自己所用,所以说环境变量可从父 Shell 传给子 Shell。不难理解,环境变量还可以传递给孙 Shell。

注意,环境变量只能向下传递而不能向上传递,即“传子不传父”。

在一个 Shell 中创建子 Shell 最简单的方式是运行 bash 命令,如图所示。
Shell脚本编程(二):shell变量
通过exit命令可以一层一层地退出 Shell。
下面演示一下环境变量的使用:
$ a=      #定义一个全局变量
$ echo $a #在当前Shell中输出a,成功 $ bash #进入子Shell
$ echo $a #在子Shell中输出a,失败 $ exit #退出子Shell
exit
$ export a #将a导出为环境变量
$ bash #重新进入子Shell
$ echo $a #在子Shell中再次输出a,成功 $ exit #退出子Shell
exit
$ exit #退出父Shell,结束整个Shell会话
可以发现,默认情况下,a 在子 Shell 中是无效的;使用 export 将 a 导出为环境变量后,在子 Shell 中就可以使用了。
export a这种形式是在定义变量 a 以后再将它导出为环境变量,如果想在定义的同时导出为环境变量,可以写作export a=22
注意,本节我们一直强调的是环境变量在子 Shell 中有效,并没有说它在所有的 Shell 种有效;如果你通过终端创建一个新的 Shell,那么它就不是当前 Shell 的子 Shell,环境变量对这个 Shell 就是无效的。
此外,通过export命令导出的环境变量是临时的,关闭 Shell 会话后它就销毁了。所以,这种环境变量也只是在局部范围内起作用,并不影响所有 Shell。
如果想让环境变量在所有 Shell 中都有效,并且能够永久保存,在关闭 Shell 后也不丢失,那么就需要把环境变量写入启动文件。至于如何写入文件,请大家自行百度,这里就不再赘述了。

Shell字符串

字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似。

单引号

str='this is a string'

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
  • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。

双引号

#!/bin/bash
ordernum=''
# 双引号里既可以有变量又可以有转义
orderdetail="this is the order $ordernum detail info, find it by \\"
echo $ordernum
echo $orderdetail

输出结果为:

this is the order  detail info, find it by \

双引号的优点:

  • 双引号里可以有变量
  • 双引号里可以出现转义字符

拼接字符串

#!/bin/bash
datestr='2018-01-01'
timestr='11:11:11'
# 双引号拼接
echo '双引号拼接字符串'
currentdatetime_1="now time is "$datestr" "$timestr" !"
currentdatetime_2="now time is ${datestr}+${timestr} 。"
echo $currentdatetime_1 $currentdatetime_2
# 单引号拼接
echo '单引号拼接字符串'
currentdatetime_11='now time is '$datestr' '$timestr' !'
currentdatetime_22='now time is ${datestr}+${timestr} 。'
echo $currentdatetime_11 $currentdatetime_22

输出结果为:

双引号拼接字符串
now time is -- :: ! now time is --+:: 。
单引号拼接字符串
now time is -- :: ! now time is ${datestr}+${timestr} 。

获取字符串长度

#!/bin/bash
datestr='2018-01-01'
echo $datestr 的长度为: ${#datestr}
结果
-- 的长度为: 

提取子字符串(截取)

以下实例从字符串第 1 个字符开始截取 4 个字符:

#!/bin/bash
datestr='2018-01-02'
echo 日期【 $datestr 】的年为: ${datestr::}

结果

日期【 -- 】的年为: 

查找子字符串

查找字符 - 或 : 的位置(哪个先出现就计算哪个):

#!/bin/bash
datestr='2018-01-02 11:11:11'
echo `expr index "$datestr" -:`

结果:

5     

注意: 以上脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。

Shell 数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。

类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。

定义数组

在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

数组名=(值1 值2 ... 值n)

例如:

array_name=(value0 value1 value2 value3)

或者

array_name=(
value0
value1
value2
value3
)

还可以单独定义数组的各个分量:

array_name[]=value0
array_name[]=value1
array_name[n]=valuen

可以不使用连续的下标,而且下标的范围没有限制。

读取数组

读取数组元素值的一般格式是:

${数组名[下标]}

例如:

valuen=${array_name[n]}

使用 @ 符号可以获取数组中的所有元素,例如:

echo ${array_name[@]}

获取数组的长度

获取数组长度的方法与获取字符串长度的方法相同,例如:

# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

Shell 注释

以 # 开头的行就是注释,会被解释器忽略。

通过每一行加一个 # 号设置多行注释,像这样:

#--------------------------------------------
# 这是一个注释
# author:菜鸟教程
# site:www.runoob.com
# slogan:学的不仅是技术,更是梦想!
#--------------------------------------------
##### 用户配置区 开始 #####
#
#
# 这里可以添加脚本描述信息
#
#
##### 用户配置区 结束 #####

如果在开发过程中,遇到大段的代码需要临时注释起来,过一会儿又取消注释,怎么办呢?

每一行加个#符号太费力了,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果。

多行注释

多行注释还可以使用以下格式:

:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF 也可以使用其他符号:

:<<'
注释内容...
注释内容...
注释内容...
' :<<!
注释内容...
注释内容...
注释内容...
!