shell编程之变量

一、变量

1.变量的命名

shell中的变量必须以字母或者下划线开头,后面可以跟数字、字母和下划线,变量长度没有限制。

#正确的变量命名

firstname

FIRSTNAME

_helloworld

Fullname

big_data

Fullname

Person01

#错误的变量命名

51paly #变量不能以数字开头

*badname #变量不能以特殊字符开头

PS1 #变量不能以特殊字符开头

for #变量不能使用shell关键字

按照以上的变量命名规则定义变量abc,从理论上来说是可行的,但是一个好的习惯是变量最好能表明它代表的含义。比如说Student_ID,一看就知道它所表达的是“学号"的意思,绝对比number这种模棱两可的变量要清晰得多名,不仅看代码的人觉得简单明了,而且有利于后期的代码维护。更好地习惯则是加上一些注释,但是也不要太过拘泥,如下所示:

#定义学号   #使用注释解释变量使后期阅读更为清晰

Student_ID

#定义日期 #这种注释就显得有所拘泥

DATE

2.变量的赋值和取值

#定义变量:变量名=变量值

#注意点一:变量名和变量值之间用等号紧紧相连,之间没有任何空格

[root@Cfhost-170820-UCNK ~]# name=john
[root@Cfhost-170820-UCNK ~]# name = john
-bash: name: command not found
[root@Cfhost-170820-UCNK ~]# name="john"

#注意点二:当变量中有空格时必须用引号括起,否则会出现错误

#其中的引号可以是双引号,也可以是单引号

[root@Cfhost-170820-UCNK ~]# name="john wang"
[root@Cfhost-170820-UCNK ~]# name=john wang
-bash: wang: command not found

变量取值很简单,只需要在变量名前加上$符号即可,严谨一点的写法是${},如下所示:

[root@Cfhost-170820-UCNK ~]# echo $name
john wang
[root@Cfhost-170820-UCNK ~]# echo ${name}
john wang

#使用${}获取变量值是一种相对比较保险的方式

[root@Cfhost-170820-UCNK ~]# name=john
[root@Cfhost-170820-UCNK ~]# name1="$name"
[root@Cfhost-170820-UCNK ~]# echo $name1
john
[root@Cfhost-170820-UCNK ~]# name1='$name'
[root@Cfhost-170820-UCNK ~]# echo name1
name1

由于Shell具有“弱变量"的特性,因此即便在没有预先声明变量的时候也可以引用的,而且没有任何报错或提醒,这可能会造成脚本中引用不正确的变量,从而导致脚本异常,但是却很难找出原因。在这种情况下,可以设置脚本运行时必须遵循“先声明再使用"的原则,这样一旦脚本中出现使用未声明的变量的情况则立刻报错。

[root@Cfhost-170820-UCNK ~]# echo $unDefinedVar

#因为该变量未声明,所以值为空,但没有任何报错

#设置变量必须先声明再使用

[root@Cfhost-170820-UCNK ~]# shopt -s -o nounset
[root@Cfhost-170820-UCNK ~]# echo $unDefinedVar
-bash: unDefinedVar: unbound variable

3.取消变量

[root@Cfhost-170820-UCNK ~]# name=john
[root@Cfhost-170820-UCNK ~]# echo $name
john #此时变量有值

[root@Cfhost-170820-UCNK ~]# unset name
[root@Cfhost-170820-UCNK ~]# echo $name
-bash: name: unbound variable

#报错为没有绑定变量

#取消函数

unset_function(){
echo "Hello,World"

}

unset unset_function

unset_function #由于函数已经被取消,这里调用会出错

 

4.特殊变量

Shell中还有一些预先定义的特殊只读变量,它们的值只有在脚本运行时才能确定。首先是”位置参数“,位置参数的命名简单直接,比如,脚本本身为$0,第一个参数为$1,第二个参数为$2,第三个参数为$3,以此类推。当位置参数的个数大于9时,需要用${}括起来标识,比如说第10个位置参数应该记为${10}。另外,$#表示脚本参数的个数总和,$@或$*表示脚本的所有参数。请看下面示例:

[root@Cfhost-170820-UCNK ~]# cat posion.sh
#!/bin/bash
echo "This script is name is: $0"
echo "$# Parameters in total"
echo "All parameters list as: $@"
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The third parameter is $3"
[root@Cfhost-170820-UCNK ~]# bash posion.sh a b c
This script is name is: posion.sh
3 Parameters in total
All parameters list as: a b c
The first parameter is a
The second parameter is b
The third parameter is c

脚本或命令返回值:$?

在管理员登录到系统中交互式地输入命令时,系统也会及时在屏幕上输出内容给予反馈。比如说本想使用ifconfig查看网卡状态,但是将命令错写成ifconfi,系统会立刻给出command not found 的提示,这种提示确实能让管理员感觉到系统非常友好。

[root@Cfhost-170820-UCNK ~]# ifconfi
-bash: ifconfi: command not found

 

但是在很多后台脚本是需要每天自动运行的,比如说每天凌晨两点的数据库备份。在这种情况下一旦出错是不可能在第一时间知道的。那靠什么判断出错呢?

再考虑一个场景:有些自动备份脚本在按时完成本地数据备份后,还会复制一份放到远程主机上(通过scp就可以做到)。不过在复制前需要先确认远程主机是否还”活着",这可以通过ping远程主机做到。如果能ping通则进行复制,如果ping不通则采取其他动作。这里又如何判断是否ping成功了呢?

这时就需要借助命令的返回值来判断了。Linux中规定正常退出的命令和脚本应该以0作为返回值,任何非0的返回值都表示命令未正确退出或未正常执行。

在第一个例子中,输错命令后立即查看当时特殊变量$?的值为127;第二个例子中,ping不通某个地址时查看当时的$?值为1.注意,$?永远是上一个命令的返回值,所以要查看某个

命令的返回值必须在运行该命令后立即查看$?。在自动化脚本中,也可以通过$?变量的值判断之前命令的执行状态,从而采取不同的动作。

#输入错误的命令时的返回值

[root@Cfhost-170820-UCNK ~]# ifconfi
-bash: ifconfi: command not found
[root@Cfhost-170820-UCNK ~]# echo $?
127

 #尝试ping主机ping不通时的返回值

[root@Cfhost-170820-UCNK ~]# ping 192.168.160.66 -c 1
PING 192.168.160.66 (192.168.160.66) 56(84) bytes of data.

--- 192.168.160.66 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 10000ms

[root@Cfhost-170820-UCNK ~]# ^C
[root@Cfhost-170820-UCNK ~]# echo $?
130

5.数组

数组是一种特殊的数据结构,其中每一项被称为一个元素,对于每个元素,都可以用索引方式取出元素的值。使用数组的典型场景是一次性要记录很多类型相同的数据时(但不是说一定要相同,因为Shell变量是弱类型的,并不要求数组的每个元素都是相同类型)。比如,为了记录班级中所有人的计算机成绩,如果不用数组来处理就只能定义所有人成绩的变量,这就会显得非常繁琐。Shell中的数组对元素个数没有限制,但是只支持一维数组,这一点和许多语言不同。

a.数组的定义

数组的定义方法如下:用declare命令定义数组Array,并将第一个元素赋值为0,第二个元素赋值为1,其下标(也就是索引)则分别是0和1(记住数组的索引从0开始计数的),然后打印出第一个元素的值

#定义名为Array的索引数组

[root@Cfhost-170820-UCNK ~]# declare -a Array

#数组的下标从0开始计数,定义了第一个元素值为0,第二个元素值为1

[root@Cfhost-170820-UCNK ~]# Array[0]=0
[root@Cfhost-170820-UCNK ~]# Array[1]=1

如果说数组Array的前两个元素“类型相同"(严格意义上这么说是不对的),那么第三个元素就显得”另类"了:赋值为一个字符串。这又一次验证了Shell变量是弱类型的,这在很多语言中是不可能的。

[root@Cfhost-170820-UCNK ~]# Array[2]="HelloWorld"

和其他变量一样,Shell对于数组变量的声明也非常宽松,而且随时都可以根据需要增加变量中的元素。相比其他语言,Shell的数据更为灵活。在很多语言中,一旦对数组进行初始化就不能改变其大小了。

[root@Cfhost-170820-UCNK ~]# declare -a Array
[root@Cfhost-170820-UCNK ~]# Array[0]=0
[root@Cfhost-170820-UCNK ~]# Array[1]=1
[root@Cfhost-170820-UCNK ~]# Array[2]"HelloWorld"
-bash: Array[2]HelloWorld: command not found
[root@Cfhost-170820-UCNK ~]# Array[2]="HelloWorld"
[root@Cfhost-170820-UCNK ~]# declare -a Name=('john','sue')
[root@Cfhost-170820-UCNK ~]# Name[2]='wang'
[root@Cfhost-170820-UCNK ~]# Name=('john' 'sue')
[root@Cfhost-170820-UCNK ~]# Score=([3]=3 [5]=5 [7]=7)

#数组在创建的时候同时赋值

[root@Cfhost-170820-UCNK ~]# declare -a Name=('john' 'sue')

#增加元素

[root@Cfhost-170820-UCNK ~]# Name[2]='wang'

#更简单的创建数组的方式--不使用declare关键字

[root@Cfhost-170820-UCNK ~]# Name=('john' 'sue')

还可以给特定的元素赋值。下面的实例就是只对第四个、第六个、第八个元素进行赋值

#跳号赋值

[root@Cfhost-170820-UCNK ~]# Score=([3]=3 [5]=5 [7]=7)

#数组取值:知道了如何定义数组和赋值元素后,下面就要了解数组一些常见操作。最简单的操作就是数组取值,其格式为:${数组名[索引]}.比之前定义的数组Array、Name为例,取值演示如下·:

[root@Cfhost-170820-UCNK ~]# echo ${Array[0]}
0
[root@Cfhost-170820-UCNK ~]# echo ${Array[2]}
HelloWorld
[root@Cfhost-170820-UCNK ~]# echo ${Name[0]}
john
[root@Cfhost-170820-UCNK ~]# echo

[root@Cfhost-170820-UCNK ~]# echo ${Name[1]}
sue

#指定索引,只能取单个值,要是想一次性取出所有元素的值,可以采取以下两种方式:
[root@Cfhost-170820-UCNK ~]# echo ${Array[@]}
0 1 HelloWorld
[root@Cfhost-170820-UCNK ~]# echo ${Array[*]}
0 1 HelloWorld

从表面上看两者没有什么区别,但是${Array[@]}得到的是以空格隔开的元素值,而${Array[*]}的输出是整个字符串。

数组长度:即数组个数。利用"@"或"*“字符,可以将数组扩展成列表,然后使用"#"来获取数组元素的个数,如下所示:

[root@Cfhost-170820-UCNK ~]# echo ${#Array[@]}
3
[root@Cfhost-170820-UCNK ~]# echo ${#Array[*]}
3

如果某个元素是字符串,还可以通过指定索引的方式获得该元素长度,如下所示:

[root@Cfhost-170820-UCNK ~]# echo ${#Array[2]}
10

 数组截取:可以截取某个元素的一部分,对象可以是整个数组或某个元素。

#取出数组的第一、第二个元素

[root@Cfhost-170820-UCNK ~]# echo ${Array[@]:1:2}
1 HelloWorld

#取出第二个元素从第0个字符开始连续5个字符
[root@Cfhost-170820-UCNK ~]# echo ${Array[2]:0:5}
Hello

#l连接数组:将n个数组进行拼接操作

[root@Cfhost-170820-UCNK ~]# Conn=($Array[@] ${Name[@]})
[root@Cfhost-170820-UCNK ~]# echo ${Conn[@]}
0[@] john sue

 

 

#取消数组或元素:和取消一般变量一样,取消一个数组的方式也可以使用unset命令

#取消数组中的一个元素
[root@Cfhost-170820-UCNK ~]# unset Array[1]

#取消整个数组

[root@Cfhost-170820-UCNK ~]# echo ${Array[@]}
0 HelloWorld
[root@Cfhost-170820-UCNK ~]# unset Array
[root@Cfhost-170820-UCNK ~]# echo

[root@Cfhost-170820-UCNK ~]# 4{Array[@]}
-bash: 4{Array[@]}: command not found
[root@Cfhost-170820-UCNK ~]# echo ${Array[@]}

 6.只读变量

只读变量又称常量,是通过readonly内建命令创建的变量。这种变量在声明时就·要求赋值,并且之后无法修改,这和之前讲的使用declare -r 声明只读变量的效果是一致的。

[root@Cfhost-170820-UCNK ~]# readonly RO=100
[root@Cfhost-170820-UCNK ~]# RO=200
-bash: RO: readonly variable

 

7.变量的作用域

变量的作用域又叫命名空间,表示变量的上下文。相同的变量可以在多个命名空间中定义,并且彼此之间互不干涉,所以在一个新的命名空间中可以自定义任何变量,因为所定义的变量都只在各自命名空间中。

在Linux系统中,不同进程ID的shell默认为一个不同的命名空间。

Shell变量的作用域是在本Shell内,属于本Shell的全局变量,也就是从定义该变量的地方开始到结束,或到主动使用unset删除该变量的地方为止。在变量的作用域内,该变量是可见的,在函数内对变量也是可以访问的、可修改的,这和C语言不同。

posted @ 2017-11-28 01:11  挑战者V  阅读(756)  评论(0编辑  收藏  举报