Linux 学习笔记: Bash Script 变量操作

bash脚本中加入变量有助于快速自动化项目。此处主要总结bash脚本中对变量的 操作,以便学习和记忆。

创立变量

bash中的变量有一般的字符变量也有数组变量。 对于一般字符变量的设置如下:

var1='this is variable 1'
echo ${var1}
# this is variable 1

var2=/home/yxy
echo $var2
# /home/yxy

对于字符可以选择使用单引号或者双引号括起来,也可以直接使用字符。需要注 意的是,如果在变量的值中有空格等符号的话,则需要使用引号括起来。

在引用变量的时候,可以直接在$符号接变量名,也可以在$符号后面接大括号, 在大括号里面放上变量名。两种方式的采用主要以不产生歧义为主,但凡容易产 生歧义的地方都需要大括号。

var1='this is variable 1'
var2=/home/yxy
var3=$var2/tmp
echo $var3
# /home/yxy/tmp

var4=${var2}/tmp2
echo $var4
# /home/yxy/tmp

var5=${var3}3
echo $var5
# /home/yxy/tmp3

如果要创立数组变量则需要使用小括号括起来所有元素,每个元素之间用空格进 行区分。

array=(one two three)

引用数组元素名本身只会获得第一个元素的值,如果需要引用数组元素的特定的 元素,则应该在数组元素名后跟方括号,其中指定元素位置,需要注意数组元素 的编码是从0开始的。

array=(one two three)
echo ${array}
# one

echo ${array[0]}
# one

echo ${array[1]}
# two

在引用数组元素中的元素时,数组名和方括号都应该放在 大括号中避免歧义。对比如下代码:

array=(one two three)
echo ${array[0]}
# one

echo $array[0]
# one[0]

如果需要获取整个数组的值,则可以在方括号中加上*或者@

array=(one two three)
echo ${array[*]}
# one two three

echo ${array[@]}
# one two three

这里 ${array[*]}返回的是一个变量,而${array[@]}返回的是一个数组变量,两者异同如下:

array=(one two three)
for x in ${array[*]}; do
    echo $x
done
# one
# two
# three

for x in "${array[*]}"; do
    echo $x
done
# one two three

for x in '${array[*]}'; do
    echo $x
done
# ${array[*]}

for x in ${array[@]}; do
    echo $x
done
# one
# two
# three

for x in "${array[@]}"; do
    echo $x
done
# one
# two
# three

for x in '${array[@]}'; do
    echo $x
done
# ${array[@]}

如果不是特别声明,bash中的变量均是全局变量,如果想要设置局部变量,则应 该使用local进行声明。

x=1
echo global x:${x}
# global x:1

y=1
echo global y:${y}
# global y:1

function myfunc {
    x=2
    echo myfunc global x:${x}
    local y=2
    echo myfunc local y:${y}
}
myfunc
# myfunc global x: 2
# myfunc local y: 2

echo global x:${x}
# global x:2

echo global y:${y}
# global y: 1

如果想要删除创建的变量,则可以使用unset命令。

x=1
echo ${x}
# 1

unset x
echo ${x}
#

脚本特殊变量

在写bash脚本中,可以传入一些参数,判断运行情况等,这些信息都存在特殊变量中。

  • $0:当前脚本名,也可以理解为第0个传入参数,如果是交互式bash,则可能是'/bin/bash`
  • $n:n为数字,是第n个传入参数,如果没有第n个参数则为空
  • $#:传入脚本或者函数的参数个数
  • $*:传入脚本或函数的所有参数,存为一个变量
  • $@:传入脚本或函数的所有参数,存为一个数组
  • $?:上个命令的退出状态,或函数的返回值,对于多数函数或命令,成功执 行会返回0,而错误会返回相应的错误编号
  • $$:当前shell进程ID

变量存在检查

在脚本中有时候需要确定一个变量是否存在,而对于其是否存在可以采取不同的行为。 如果一个变量不存在,引用的话默认是null,如果用echo,则反应的是没有输出。

echo ${notexist}
#

若变量不存在引用默认值

如果只需要在引用的时候得到一个值(设定的默认值或者null),而不需要改变变量是否存在的事实,可以使用如下代码:

exist=1
echo ${exist:-'exist'}
# 1

echo ${exist-'exist'}
# 1

unset notexist
notexist=
echo ${notexist-'not set value yet'}
#

echo ${notexist:-'not set value yet'}
# not set value yet

unset notexist2
echo ${notexist2-'not exist yet'}
# not exist yet

echo ${notexist2:-'not exist yet'}
# not exist yet

从上面例子可知道,如果变量存在,则返回相应的变量的值,如果不存在,则根 据情况返回空值或者默认值。-:-的差别在于如果变量已经声明而没有赋 值(notexist=),则${notexist-'not'}返回的也是null,即空行,而 ${notexist:-'not'}则返回默认值。而对于没有声明的变量而言,两者表现一 致。

若变量存在则返回默认值

上面的例子是在变量不存在的时候引用默认值,也可以设置变量存在引用默认值, 而如果变量不存在则引用空值。

exist=1
echo ${exist:+'exist'}
# exist

echo ${exist+'exist'}
# exist

unset notexist
notexist=
echo ${notexist+'not set value yet'}
# not set value yet

echo ${notexist:+'not set value yet'}
#

unset notexist2
echo ${notexist2:+'not exist yet'}
echo ${notexist2+'not exist yet'}

同样,+:+的差别在于如果变量已经声明而没有赋值(notexist=),则 ${notexist+'not'}返回的是变量存在的默认值,而${notexist:+'not'}则 返回空。而对于没有声明的变量而言,两者表现一致。从这里可以看出来, :在这里起到的作用和上小节例子中类似,可以理解为-+检查的是变量 是否声明(var=),而:-:+检查的是变量值是否为空(null),变量声 明而未赋值的话则变量值为空,变量未声明值也为空。

设置变量默认值

如果变量不存在需要对变量进行赋值的话,则可以使用如下代码:

unset notexist
notexist=
echo ${notexist='now set value'}
#

echo ${notexist}
#

unset notexist2
notexist2=
echo ${notexist2:='now set value'}
# now set value

echo ${notexist2}
# now set value

unset notexist3
echo ${notexist3='now exist'}
# now exist

echo ${notexist3}
# now exist

unset notexist4
echo ${notexist4:='now exist'}
# now exist

echo ${notexist4}
# now exist

在这里的:的差别和上面一样,=检查的是变量是否存在,而:=检查的是变量的值是否为空。

若变量不存在则报错

有时候需要给出变量不存在的提醒,而如果变量存在则返回变量的值。如下代码可以实现:

exist=1
echo ${exist?'Error, not exist'}
# 1

echo ${exist:?'Error, not exist'}
# 1

unset notexist
notexist=
echo ${notexist?'Error, not exist'}
#

echo ${notexist:?'Error, not exist'}
# bash: notexist: Error, not exist

unset notexist2
echo ${notexist2?'Error, not exist'}
# bash: notexist2: Error, not exist

echo ${notexist2:?'Error, not exist'}
# bash: notexist2: Error, not exist

对于声明而未赋值的变量来说?返回的是空值,而:?则报错,对于未声明的 变量来说?:?都会报错。

变量的长度

对于变量的值,我们可以获取变量的字符的数量,对于数组变量,我们也可以获 取数组变量元素的个数。

x=one
echo ${#x}
# 3

y=(one two)
echo ${#y}
# 3

如果y是一个数组,上面的代码返回的会是数组第一个元素的长度,而非数组的长 度。需要注意的是数组是从0开始计数的。

而如果要获取数组元素的数量则应该使用如下的代码:

y=(one two)
echo ${#y[*]}
# 2

echo ${#y[@]}
# 2

编辑变量的输出值

对于变量的输出值我们可以直接进行改变,这是是说引用变量时候的输出值,因为变量本身存储的值并不会发生改变。

依照匹配模式从前删除字符串

${var#Pattern} 或者 ${var##Pattern}可以删去符合Pattern的字符串,区 别在于,${var#Pattern}删去的是从变量值起始的最短匹配,而 ${var##Pattern}删去的是从变量值起始的最长匹配。

var=/one/two/three
echo ${var#*/}
# one/two/three

echo ${var##*/}
# three

Pattern可以是正则表达式字符串,也可以是存有需要匹配的字符串的变量。

依照匹配模式从后删除字符串

${var%Pattern} 或者 ${var%%Pattern}可以删去符合Pattern的字符串,区 别在于,${var%Pattern}删去的是从变量值末尾的最短匹配,而 ${var%%Pattern}删去的是从变量值末尾开始的最长匹配。这与#从前删除正 好相反。

var=/one/two/three
echo ${var%/*}
# /one/two

echo ${var%%/*}
#

#% 的例子可以看出来,一个符号对应的只删除最短匹配,而两个符 号对应的删除最长匹配。

按照位置提取变量值中的字符串

对于变量值中的字符串,也可以按照其位置输出。变量值字符串是从0开始编码 的,${var:start:length}或者${var:start}即可以输出相应位置的字符串。 第一个冒号后面是字符串起始位置,第二个冒号后面是输出的字符串长度,如果 省略长度,则默认输出剩下所有字符串。

var=012345678
echo ${var:0}
# 012345678

echo ${var:5}
# 5678

echo ${var:5:2}
# 56

替换

引用变量的返回值可以直接根据Pattern进行替换, ${var/Pattern/Replacement}${var//Pattern/Replacement}均可以把字 符串中符合Pattern的部分替换为Replacement代表的字符,如果Replacement为 空的话,起到的效果相当于删除相应的字符串。 ${var/Pattern/Replacement}是替换从变量字符串起始找到的第一个匹配,而 ${var//Pattern/Replacement}则是替换找到的所有匹配。

var=one-two-thre-one-two-three
echo ${var/one/1}
# 1-two-thre-one-two-three

echo ${var//one/1}
# 1-two-thre-1-two-three

echo ${var/o*o/1}
# 1-three

echo ${var//o*o/1}
# 1-three

从上面代码的输出结果可以知道,对于没有通配符的字符串的匹配,///的结果很明确不同,而如果使用了通配符,则结果会变得相同,符合程序的 一致性逻辑,而于直觉略不同,这里需要稍稍注意。

如果只想要替换字符串中匹配的开头或者结尾,而不替换中间的部分,则可以使 用${var/#Pattern/Replacement}${var/%Pattern/Replacement}/#用 于匹配开头,/%用于匹配结尾。

var=one-two-thre-one-two-three
echo ${var/#one/1}
# 1-two-thre-one-two-three

echo ${var/%one/1}
# one-two-thre-one-two-three

echo ${var/#three/3}
# one-two-thre-one-two-three

echo ${var/%three/3}
# one-two-thre-one-two-3

从这个例子可以看出来,用于替换的字符/#/%与用于删除的字符#%的功能有一致性,#均指代从字符串开头开始,而%指代从字符串末尾开始。

输出符合一定规律的变量名

如果我们只需要获取符合一定规律的所有变量的变量名,可以使用${!nameprefix*}${!nameprefix@}

var1=1
var2=2
var3=3
varnames=${!var*}
echo variable names: ${varnames}
# variable names: var1 var2 var3

echo length: ${#varnames}
# length: 14

需要注意的${!nameprefix*}是返回的均是一个字符串,而${!nameprefix@}返回的是一个数组。如下面的例子:

var1=1
var2=2
var3=3
for x in '${!var*}'; do
    echo $x
done
# ${!var*}

for x in "${!var*}"; do
    echo $x
done
# var1 var2 var3

for x in '${!var@}'; do
    echo $x
done
# ${!var@}

for x in "${!var@}"; do
    echo $x
done
# var1
# var2
# var3
By @Wolfson Liu in
Tags : #linux,