shell 脚本之变量、数组、扩展

变量

变量是什么

在Bash shell中,只有一种数据型态:以字符组成的字符串。Bash Shell把任何存储在变量中的值,皆视为以字符组成的“字符串”。
要使用Bash Shell变量,不需要经过实现声明。

每个Shell环境都会维护一份它们自己的变量空间,彼此不互相影响。

变量命名

变量名可用英文字母、数字及下划线组成,但开头第一个字符不能是数字;变量名长度不限制;大小写敏感;

变量赋值

给变量赋值时,等号两边不可以有空白;若变量值有空白,则要用引号括起来。

变量和引号

推荐习惯:把字符串用双引号或单引号括起来。
在双引号中,可进行以下3个操作:替换变量;替换命令执行结果;替换算术运算结果。

单引号的作用是形成一个所见即所得的字符串,不会进行任何变量替换的操作。

取变量值

$变量名${变量名}
前者在变量名后面是空白符时使用,后者在变量名与其他字母、数字、下划线混合时使用。

取消变量:

取消变量就是把变量从变量空间删除释放。


unset 变量名
unset -v 变量名
unset -f 函数名

清空变量

变量仍然存在,只不过值是长度为0的字符串。

变量名=

环境变量

父shell输出环境变量后,后续的各种操作包括子shell都可提取该变量的值。
export var="var value"
declare -x envVar="var value"

bash内置变量


CDPATH: cd命令的搜索路径,自定义CDPATH,可缩短键入路径文件名的长度。
IFS:定义字段分隔符。默认为:空格符、tab字符、换行符。
OLDPWD:前一个工作目录。


$0:执行程序的名称。
$1-$n:位置参数。传入程序或函数的参数,第一个是$1, n超过9的,用${n}
$*:代表所有位置参数,并且视为一个字符串。
$@:代表所有位置参数,但$@代表各位置参数的串行。
$#:位置参数的个数。
$-:bash shell目前使用的功能选项。
$?:上一个命令执行结束后的返回结果。通常0表示成功,非0失败。
$$:目前shell的进程编号。
$!:上一个后台程序的进程编号。

$_:用途有3种:
  1.  script执行时,bash的绝对路径;
  2.  上一个命令执行时,最后一个位置参数;
  3.  检查邮件时,值为邮件文件名。

调整变量属性

readonly用法

readonly 用于设置只读变量。
readonly 变量名
readonly -f 函数名
readonly -a 数组名

declare用法


选项        用途                               范例
-p        显示变量属性                      declare -p x
-a        变量是个数组                      declare -a arr
-f        搜索命令时,只找函数          declare -f showit
-i        变量是整数                          declare -i intvar
-F        显示所有函数名称及其属性    declare -F
-r        设定只读变量                      declare -r a
-t        设置变量具有trace属性         declare -t xyz=11
-x        设为环境变量                     declare -x PATH

举例

代码:


var="i am user defined variable ."
vartoo="$var too ."
echo "lets see their value, var:\"$var\", vartoo:\"${vartoo}\""
echo 'we cannot see their value, var:\"$var\", vartoo:\"${vartoo}\"'

unset vartoo
echo "after unset vartoo, lets see their value, var:\"$var\", vartoo:\"${vartoo}\""

declare -i int=123
echo "i am int type value is : $int"
int="given"
echo "after giving a string value, what is int value: $int"

结果:


lets see their value, var:"i am user defined variable .", vartoo:"i am user defined variable . too ."
we cannot see their value, var:\"$var\", vartoo:\"${vartoo}\"
after unset vartoo, lets see their value, var:"i am user defined variable .", vartoo:""
i am int type value is : 123
after giving a string value, what is int value: 0

数组

bash数组的元素个数没有限制,索引从0开始,不一定要连续。

建立数组:

一次设定每个元素值: B=(2 3 5 4),元素之间用空格分隔。
指定个别元素的索引: C=([3]=77 [1]=88 [5]=12)
指定某一元素索引,其余由系统接续: E=(124 [8]=188 266)

取值

${数组名[索引]}: 取元素值。

${arr[@]}:一次取出数组所有元素,以空白分隔每个元素;如果元素是字符串,且值含有空格,应该通过数组的索引列表进行遍历(应该总是通过索引列表进行遍历)。
${arr[*]}:一次取出数组所有元素,连接成一个字符串。

取数组索引列表

${!数组变量[@]}${!数组变量[*]}
把数组变量的所有索引列出,各索引值之间用$IFS定义的第一个分隔符隔开。

计算数组长度: ${#arr[@]} 或 ${#arr[*]}
计算数组某个元素的长度: ${#arr[索引]}

取消数组或数组元素

unset 数组名
unset 数组名[索引]

举例

代码:


arr=(3 2 404 0 5)
echo "arr length is ${#arr[@]},  ${#arr[*]}, length of element 2 is ${#arr[2]}"

unset arr[2]
echo "after unset arr[2], arr length is ${#arr[@]},  ${#arr[*]}, length of element 2 is ${#arr[2]}"

arr2=([3]="x y z" [1]=1 [5]=911)
echo -n "iterate arr2 element set: "
for i in ${arr2[@]}
do
    echo -n $i ",,"
done
echo ""

echo -n "iterate arr2 by index set : "
for i in ${!arr2[@]}
do
    printf "%s," "${arr2[$i]}"
done
echo ""

结果:


arr length is 5,  5, length of element 2 is 3
after unset arr[2], arr length is 4,  4, length of element 2 is 0
iterate arr2 element set: 1 ,,x ,,y ,,z ,,911 ,,
iterate arr2 by index set : 1,x y z,911,

here document

基本语法


命令 << 标记
........content ...
标记

这样会把命令和标记之间的内容,利用转向输入的方式交给该命令去处理。

标记可以是任何字符串,尽量选用不常用的字符组合,避免该字符串出现在输入的内容中造成bash误判。

bash支持变量替换,对标记加上单引号可关闭变量替换($var),但命令扩展(反引号扩展)仍然是可以的。

bash中:命令表示什么也不做,所以可以结合here document进行注释。

在标记前加上-,表示抑制各行首TAB的作用。-<<及标记之间不能有空格。

举例


: << coment_label
        comment line 1
        comment line 2
coment_label

sh <<-cmd_label
        echo "call date command from here document :`date`"
        echo "use var replace from here document :$var"
cmd_label

sh <<-'cmd_label'
        echo "call date command from here document :`date`"
        echo "use var replace from here document :$var"
cmd_label

变量扩展



条件式                    目的                                    处理
${var:-默认值}          回传的结果一定要有值           如果var不存在或为空值,返回默认值,否则返回变量值。
${var:=默认值}        给空值变量设置一个默认值      如果var不存在或为空值,把var设为默认值。
${var:?错误信息}      检查条件是否完备                  如果var不存在或为空值,显示'变量名称:提示信息,停止执行script。
${var:+默认值}        判断某事是否为真                  Var存在且非空,返回默认值,否则返回变量值。

在上表各式中,若移除:,则只测试存在性,不测空值。

记忆法:


:      空          测空值
-      负向       测不存在
=     设置       给空值变量设一个默认值
?     有问题     检查条件是否完备后再执行
+     正向       测试存在

变量扩展

字符串的位置下标由0开始。

取部分位置参数

${@:起始位置}
${@:起始位置:个数}

取子串

${var:起始位置}
${var:起始位置:长度}

计算字符串长度

${#变量名}expr length "$变量名"

字符串截取

${变量#样式}:从第一个字符开始匹配,删除最短匹配。
${变量##样式}:从第一个字符开始匹配,删除最长匹配。
${变量%样式}:从最后一个字符开始,删除最短匹配。
${变量%%样式}: 从最后一个字符开始,删除最长匹配。

如果第一个字符或最后一个字符开始的字符串与样式不匹配,则不截取。

一种记忆方法:在键盘上#%的左边,所以#是从左开始,%是从右开始;%%%短,所以%是最短匹配,%%是最长匹配。

字符串替换

${var/pattern/replace_str}:只替换第一个最长匹配。
${var//pattern/replace_str}:替换所有的最长匹配。
${var/#pattern/replace_str}:只从var开头匹配,用replace_str替换最长匹配。
${var/%pattern/replace_str}:只从var结尾匹配,用replace_str替换最长匹配。
replace_str可以是变量,如:${var/pattern/var2}

上述替换串为空则表示删除匹配。

举例

代码:


str="hello world ."
echo "str=\"${str}\",length=${#str}" ',${str:4}=' "\"${str:4}\"" ',${str:4:4}=' "\"${str:4:5}\""

str2="xx x"
echo "\"${str/e*o/$str2}\", \"${str/e*o/------}\", \"${str/e*o/}\""
echo "${str/#h*o /abc},   ${str/%w*d ./$str2} "

echo '${str#e*o}=' "\"${str#e*o}\"" ',${str##e*o}=' "\"${str##e*o}\""
echo '${str#h*o}=' "\"${str#h*o}\"" ',${str##h*o}=' "\"${str##h*o}\""
echo '${str%w*o}=' "\"${str%w*o}\"" ',${str%%w*o}=' "\"${str%%w*o}\""
echo '${str%w*.}=' "\"${str%w*.}\"" ',${str%%w*.}=' "\"${str%%w*.}\""

结果:


str="hello world .",length=13 ,${str:4}= "o world ." ,${str:4:4}= "o wor"
"hxx xrld .", "h------rld .", "hrld ."
abcworld .,   hello xx x
${str#e*o}= "hello world ." ,${str##e*o}= "hello world ."
${str#h*o}= " world ." ,${str##h*o}= "rld ."
${str%w*o}= "hello world ." ,${str%%w*o}= "hello world ."
${str%w*.}= "hello " ,${str%%w*.}= "hello "

取变量名称列表

${!开头字符串@}${!开头字符串*}
把所有以指定字符串开头的变量名列出,各变量之间用$IFS定义的第一个分隔符隔开。

命令替换

命令替换是把命令执行后的标准输出放入变量中。


var=$(命令)
var=`命令`

在这个过程中,默认会自动删除命令结果里的换行符。

算术运算


名称                           语法                              范例
算术扩展                     $((算术式))                     r=$((2+5*8))  如果表达式有变量,变量名前不要加$
使用外部程序expr         expr 算术式                     r=`expr 4+5`
使用$[]                      $[算术式]                        R=$[4+5]
使用内置命令declare     declare -i var=算术式         declare -i r=8+6
使用内置命令let           let 算术式                        let r=5+6

在求值时,如果变量不存在或空值,则其值为0.

expr做算术运算时,如果表达式里含有shell的特殊字符,需要进行转义。expr在我的ubuntu12.04 vps上没效果。

使用declarelet的注意事项:
算式中,运算符和操作数之间不可以有空格,要紧密连接;
特殊符号不必使用\转义;
算式中可包含其他变量,变量前不必加$

从各种限制来看,还是使用$((expression))最好。

举例

代码:


declare -i int=123
declare -i int2=123*2

r=$(( int * 3 ))
r4=$[$int * 4]
r5=`expr 4*5`
let r6=$int*6
#r5=`expr $int*5`
echo '$int=' $int '$,int2=' $int2 ',$r=' $r
echo '$r4=' $r4 ',$r5=' $r5 ',$r6=' $r6

结果:


int= 123 $,int2= 246 ,$r= 369
$r4= 492 ,$r5= 4*5 ,$r6= 738

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据