简介
Shell 是指一种命令行解释器,为用户和操作系统之间进行通信提供一种接口,它接受来自用户的命令,并将其转换成一系列的系统调用送到内核执行,并将结果输出给用户。Shell 不仅是一种解释器,还是一种编程工具,脚本语言。Shell 有很多种,本系列文章只介绍 Bash Shell。
变量命名
Shell 的变量命名和大多数语言一样,由字母,数字和下划线组成,第一个字符只能是字母或下划线。Shell 变量区分大小写,不能使用系统关键字。
变量创建、使用和回收
- Shell 变量是“弱类型”变量,同个变量可以赋不同类型的值
- 变量不需要定义,也不需要先声明再使用,第一次赋值就是声明变量
[root@localhost ~]# name="lin"
[root@localhost ~]# echo $name
变量赋值直接用一个等号把变量和值关联起来,中间不能有空格;赋字符串值时可以没有引号,也可以使用单引号和双引号,但如果是把变量赋给变量的话,那必须使用双引号,因为单引号解析不了 。给变量赋值时不需要 符号,取值时则需要。
[root@localhost ~]# name=lin
[root@localhost ~]# echo $name
[root@localhost ~]# name='jin'
[root@localhost ~]# echo $name
[root@localhost ~]# name="xin"
[root@localhost ~]# echo $name
[root@localhost ~]# name=1
[root@localhost ~]# name_2=$name
[root@localhost ~]# name_3="$name"
[root@localhost ~]# name_4='$name'
[root@localhost ~]# echo $name $name_2 $name_3 $name_4
最后一个命令的输出是 1 1 1 $name
,$name_2 和 $name_3 可以直接取到 $name 的值,但 $name_4 则取不到,它得到的值是字符串 $name,所以它的值就是 $name 而不是 1。
如果要回收变量,使用 unset 命令来取消变量,它的内存也会被释放,函数也可以取消。
[root@localhost ~]# name=true
[root@localhost ~]# echo $name
[root@localhost ~]# unset name
[root@localhost ~]# echo $name
[root@localhost ~]# foo()
[root@localhost ~]> {
[root@localhost ~]> echo 'hello world'
[root@localhost ~]> }
[root@localhost ~]# foo
[root@localhost ~]# unset foo
[root@localhost ~]# foo
第一次输出变量 name 的值是输出 true,第二次输出什么都没有,因为 name 已经被回收了;第一次调用 foo 函数时会打印出 ‘hello world’,第二次调用会提示命令不存在,因为函数 foo 已经被回收了。
变量不需要先声明再使用,如果变量未声明(即没赋过值)就开始使用,那得到的值会是空的,但不会报错。如果想要提高脚本的严谨性,可以强制要求变量必须先声明再使用,如果使用未声明的变量则会报错。
[root@localhost ~]# echo $undefined_var
[root@localhost ~]# shopt -s -o nounset
[root@localhost ~]# echo $undefined_var
第一个命令什么也没输出,但不会报错;第二个命令把当前 shell 环境设置成变量必须先声明再使用;第三个命令就报错了,因为变未声明,不能取它的值。
变量的作用域
变量只作用在它的“命名空间”中,默认命名空间是指当前 shell 或当前脚本文件,不同 shell 之间以及不同脚本之间的变量是互不干扰的。在当前命名空间声明的变量,其作用域就是整个命名空间,所以在函数内部的变量和函数外部的变量,其作用域是一样的;如果要在函数内使用局限于函数内部的变量,可手动加上 local 关键字。
learn_var1.sh
#!/bin/bash
name='lin'
foo()
{
name='jin'
echo name in function is $name
}
foo
echo "name out of function is" $name
exit 0
执行结果
name in function is jin
name out of function is jin
learn_var2.sh
#!/bin/bash
name='john'
foo()
{
local name=marry
echo name in function is $name
}
foo
echo name out of function is $name
exit 0
执行结果
name in function is marry
name out of function is john
第一个脚本在函数内部和外部输出的结果是一样的,这是因为函数内部修改了变量的值,修改的是在函数外面定义的变量 name;而第二个脚本则输出不同的结果,因为函数内部使用 lcoal 关键字声明了一个只属于自己的变量,与外面的变量相互独立。另外,两个脚本使用了相同的变量名和函数名,但不会出现相互干扰的情况,因为这是两个作用域。
变量分为两种,环境变量和局部变量。局部变量只能在当前 shell 中使用,因为这是它的作用域,也就是说局部变量只在其作用域内工作;环境变量不仅可以在当前 shell 使用,还可以在继承自当前 shell 的子 shell 使用。变量默认是局部变量,如果要使其成为环境变量,可以使用 export 命令将其导出。
[root@localhost ~]# name=july
[root@localhost ~]# echo $name
[root@localhost ~]# bash
[root@localhost ~]# echo $name
[root@localhost ~]# exit
[root@localhost ~]# export name
[root@localhost ~]# bash
[root@localhost ~]# echo $name
在当前 shell 中定义变量 name 并输出它的值,得到字符串 july;然后使用 bash 会打开一个新的 shell,这个 shell 是当前 shell 的子 shell,在子 shell 中输出变量 name 的值,得到一个空值。使用 exit 命令会返回到父 shell,在父 shell 中将变量 name 导出,再重新打开一个子 shell,输出变量 name 的值发现输出 july,说明此时变量已经成为环境变量了。
数组
shell 只支持一维数组,数组的索引从 0 开始,数组的大小不是固定的,可以随时扩展数组,也可以跳跃式地给元素赋值。
定义数组
使用 declare 关键字加上命令 -a 就可以声明一个数组变量,默认数组的长度为 0。如果在声明的时候就已经给数组初始化一些元素,则使用一对小括号来赋值,数组元素之间用空格隔开;声明时初始化可以省略 declare 关键字。
#!/bin/bash
declare -a arr
arr[0]=hello
arr[1]=2
arr[2]=true
echo ${arr[@]}
unset arr
declare -a arr=('good' 1011 'job')
echo ${arr[@]}
unset arr
arr=([1]='lin' [3]='jin' [5]='xin')
echo ${arr[@]}
执行结果
hello 2 true
good 1011 job
lin jin xin
取数组元素
取数组元素直接使用数组加上索引即可,取变量名的标准写法是 ${变量名}
,一般 {} 可以省略,但在某些情况下省略会出问题,比如取数组元素的时候,$arr[0]
的结果是取数组的值再连接字符串 [0]。
* 取数组某个下标元素 ${array[index]}
* 取数组所有元素(返回以空格隔开的元素值) ${array[@]}
* 取数组所有元素(返回所有元素连接起来的字符串) ${array[*]}
* 取数组长度 ${#array[@]} 或 ${#array[*]}
* 使用 @ 和 * 都可以获取数组的所有元素,返回的结果”类型“虽然不一样,但值都是所有元素拼接起来,中间用空格隔开
* # 不仅可以取数组的长度,还可以取字符串的长度
* $array 等价于 ${array[0]}
arr=(100 [1]=lin [3]=3.14 [4]=good)
echo ${arr}
echo ${arr[0]}
echo ${arr[1]}
echo ${arr[3]}
echo ${arr[@]}
echo ${arr[*]}
echo ${#arr[@]}
echo ${#arr[4]}
str=good
echo ${#str}
执行结果
100
100
lin
3.14
100 lin 3.14 good
100 lin 3.14 good
4
4
4
数组截取
可以使用 ${array[@]:start:len}
来取数组的多个元素,返回一个拼接后的字符串,取值的区间是 [start,start+len]。同样该方法也适用于字符串,用于取子字符串。
arr=(1 2 3 4 5)
echo ${arr[@]:1:3}
arr[5]=hello
echo ${arr[5]:2:2}
str=good
echo ${str:1:2}
执行结果
2 3 4
ll
oo
连接数组
可以把多个数组连接成一个新的数组,连接的方法是使用数组的初始化方法,即 array=(element1 element2),其实是创建一个新的数组,然后把原来的数组的值赋给新数组。
arr1=(10 20 0)
arr2=(100 200 300)
arr3=(${arr1[@]} ${arr2[@]})
arr4=(${arr1[*]} ${arr2[*]})
echo ${arr3[@]}
echo ${arr4[@]}
echo ${#arr3[*]}
echo ${#arr4[*]}
echo ${arr4[5]}
执行结果
10 20 30 100 200 300
10 20 30 100 200 300
6
6
300
替换数组元素
替换元素最简单的方法就是给某个下标的元素重新赋值,还有一种替换方法,不需要知道元素的下标而根据元素的值直接替换。
arr=(hi good)
arr[1]=bad
arr=(${arr[@]/hi/hello})
echo ${arr[@]}
arr=(${arr[*]/hello/你好})
echo ${arr[@]}
执行结果
hello bad
你好 bad
取消数组元素
取消数组元素和取消其它变量一样,使用 unset 关键字,如果要取消整个数组,则使用 unset 加上数组名。
arr=(1 2 3 4)
unset arr[0]
echo $arr
echo ${arr[@]}
unset arr
echo ${arr[@]}
执行结果
1:
2: 2 3 4
3:
第一个输出的结果为空,因为 $arr 取的是 arr 的第一个元素,即 arr[0],而 arr[0] 被取消了,所以输出空。第三个输出也是空,因为整个数组都被取消了。
特殊变量
位置参数
脚本执行时可带一些参数,参数的个数是不定的,如何取到这些不定的参数,就需要使用位置参数变量了。使用 $n(n为数字) 来取到脚本运行时的参数,$0 为脚本本身的名称,$1 为第一个参数,依此类推;当参数位置大于 9 时,需要使用 {} 括起来,比如取第 10 个位置(参数) 的写法是 ${10}。使用 $# 获取参数的个数,不包括 $0,也就是不包括脚本本身;$@ 或 $* 来获取所有参数,两样不包括 $0。如果取一个不存在参数的位置,则取到的是空值。
echo "the script's name is : $0"
echo "the first parameter is : $1"
echo "the second parameter is : $2"
echo "the third parameter is : $3"
echo "all the parameter is : $@"
echo "the number of parameter is : $#"
执行结果1
[root@localhost ~]# ./learn_special_var.sh -path /bin/sh/var -language shell
the script's name is : ./learn_special_var.sh
the first parameter is : -path
the second parameter is : /bin/sh/var
the third parameter is : -language
all the parameter is : -path /bin/sh/var -language shell
the number of parameter is : 4
执行结果2
[root@localhost ~]# ./learn_special_var.sh -language shell
the script's name is : ./learn_special_var.sh
the first parameter is : -language
the second parameter is : shell
the third parameter is :
all the parameter is : -language shell
the number of parameter is : 2
脚本或命令返回值
每个脚本或命令都会有一个返回值,shell 认为返回 0 才表示脚本正常执行。如果输入了一个不存在的命令,则会返回 127;ping 主机不能时返回 1。 ?用于取到返回值,我们可以在命令或脚本执行之后使用 ? 来取到脚本或命令返回值。如果我们的脚本没有显式地写返回值,则默认返回 0。
[ljx@localhost ~]$ ifconfi
bash: ifconfi: command not found
[ljx@localhost ~]$ echo $?
127
[ljx@localhost ~]$ ./learn_special_var.sh
the script's name is : ./learn_special_var.sh
the first parameter is :
the second parameter is :
the third parameter is :
all the parameter is :
the number of parameter is : 0
[ljx@localhost ~]$ echo $?
0
系统环境变量
bash 默认预定义了很多系统环境变量,下面列举一些
变量名 | 含义 |
---|---|
$BASH | 系统安装 bash shell 的全路径 |
$BASH_VERSION | |
$CDPATH | |
$EUID | |
$FUNCNAME | |
$HISTCMD | |
$HISTFILE | |
$HISTFILESIZE |
只读变量
如果声明的时候在前面加上 readonly 关键字,则该变量就是只读变量。只读变量只能在声明时赋一次值,如果声明时不赋值,那它的值就是空,后面不能再修改它的值。另外,只读变量不能被取消。
[ljx@localhost ~]$ readonly ro=10
[ljx@localhost ~]$ echo $ro
10
[ljx@localhost ~]$ ro=100
bash: ro: readonly variable
[ljx@localhost ~]$ unset ro
bash: unset: ro: cannot unset: readonly variable
[ljx@localhost ~]$ readonly rovar
[ljx@localhost ~]$ echo $rovar