【Linux】Linux脚本编程

脚本编写基础

变量和参数

变量替换

变量的名字是它的值保存的地方。引用它的值称为变量替换。如果varible1是一个变量,那么$varible1就是引用该变量的值,即这个变量包含的数据。

解释:$为变量替换符,表示引用该变量的内容。

变量赋值

用“=”对变量进行赋值,“=”的左右两边不能有空白符。

bash变量无类型

不同于许多其他编程语言,bash不以“类型”区分变量。本质上说,bash变量是字符串,但是根据环境的不同,bash允许变量有整数计算和比较操作,其中决定因素是变量的值是否只含有数字。比如:

#!/bin/sh
a="hello world"        #对变量赋值
echo "A is:"            #打印变量
echo $a                #打印变量a的内容

解释:shell脚本的第一行必须以 #!/bin/sh 格式开头,符号 #! 用来指定脚本文件的解析程序,当编译好脚本后,如果要执行该脚本,还必须使其具有可执行的属性。调用命令:

chmod +x shell脚本文件名

echo "内容"表示将文字内容打印到屏幕上。

有时变量名很容易与其他文字混淆,比如:

num=2
echo "this is the $numnd"

此时不会打印this is the 2nd的内容,而仅仅打印this is the,因为shell会去搜索变量numnd的值,但是这个变量是没有值的。可以用花括号来告诉shell所要打印的是num变量,修改如下:

echo "this is the ${num}nd"

此时打印的内容就是this is the 2nd。

解析:{}花括号,这个结构表示代码块,也就是一组命令代码块,它是匿名的函数。与函数不同的是,在代码块里的变量仍然能被脚本后面的代码访问。

局部变量

局部变量只在代码块或者一个函数里有效。如果变量用local来声明,那么它只能在该变量声明的代码块中可见。这个代码块就是局部“范围”。在一个函数内,局部变量意味着该变量只有在函数代码块内才有意义。比如:

#!/bin/bash
hello="var1"
echo $hello
function func1 {
	local hello="var2"
	echo $hello
}
func1
echo $hello

需要注意的是,function fun1 {...}其中fun1和大括号之间的空格不能省略,否则会出错。

打开终端,建立该脚本文件,运行结果如下:

[root@localhost ~]# ./test1
var1
var2
var1

从结果就能看到局部变量的使用方法。

环境变量

环境变量会影响shell的行为和用户接口,大多数情况下,每个进程都有一个环境表,它由一组被进程使用的环境变量组成。shell的其他进程也一样,每次当一个shell启动时,它都会创建新的合适的环境变量。

增加或更新一个环境变量,都会使这个shell的环境表得到更新,换句话说,更改或增加的变量会立即生效,并且这个shell的所有子进程(即它执行的命令)都能继承它的环境变量。准确的说,是后继生成的子进程才会继承,已经运行的子进程并不会得到它的新环境变量。

分配给环境变量的总空间是有限的,如果创建太多的环境变量或者有的环境变量太长而占用太多的空间则会出错。比如:

bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ'`"
bash$ du
bash:/usr/bin/du:Argument list too long(参数列表太长)
位置参数

位置参数是一种在调用Shell程序的命令行中按照各自的位置决定的变量,是在程序名之后输入的参数,它们分别标识了用户输入的整个命令行中以空格分隔开的字符串。也就是说:位置参数就是通过命令行传递给脚本的参数(类似于C++中的cin>>的内容)。

位置参数的值可以用$N得到,N是一个整数(从0开始),Linux会把输入的字符串分段(空格分段)并给每段进行编号,编号从0开始,第0号为程序名字,从1开始就表示传递给程序的参数。注意:在位置参数$9之后的参数必须要用括号括起来,例如:${10}、${11}等等。

位置参数的值总结
位置参数 含义
$0 当前这个Shell程序的文件名
$#  传递给程序的总的参数数目
$? 上一个代码或者Shell程序在Shell中退出的情况,如果正常退出则返回0,否则返回非0值
$*  传递给程序的所有参数组成的字符串
$n 表示第n个参数(n<10)
$@ 以“参数1”、“参数2”……保存所有的参数
$$ 本程序的(进程ID)PID
$!  上一个命令的PID
${n} 表示第n个参数(n>9)

例如:

#!/bin/bash
echo "number of vars:"$#
echo "values of vars:"$*
echo "value of var1:"$1
echo "value of var2:"$2
echo "value of var3:"$3
echo "value of var4:"$4

打开终端,建立该脚本文件,运行结果如下:

[root@localhost ~]# ./test1 1 2 3 4 5
number of vars:5
values of vars:1 2 3 4 5
value of var1:1
value of var2:2
value of var3:3
value of var4:4
退出和退出状态

exit命令一般用于结束一个脚本,它也能返回一个值给父进程。每一个命令都能返回一个退出状态(有时也看作返回状态)。一个命令执行成功时返回0,执行不成功时则返回一个非零值(1-255),此值通常可以被解释成一个对应的错误值。

同样的,脚本里的函数和脚本本身都会返回一个退出状态码。在脚本会程序里被执行的最后一个命令将决定退出状态码。

如果一个脚本以不带参数的exit命令结束,脚本的退出状态码将会是执行exit命令前的最后一个命令的退出码。脚本结束没有exit、不带参数的exit和exit $?三者是等价的。

其中,$?变量保存了最后一个命令执行后的退出状态。当一个函数返回时,$?保存了函数里最后一个命令的退出状态码,这就是bash里函数返回值的处理办法。当一个脚本运行结束,$?变量保存脚本的退出状态,而脚本的退出状态就是脚本中最后一个已执行命令的退出状态。

例如:

#!/bin/bash
echo "hello"
echo $?
lskdf
echo $?
exit 113

打开终端,建立该脚本文件,运行结果如下:

[root@localhost ~]# ./test1
hello
0
./test1:line4:lskdf:command not found
127

这里lskdf是无效命令,所以显示错误原因。下一行echo $?,就打印前一个命令的退出状态(127,非零)。


流程控制

对代码块的操作是构造和组织shell脚本的关键,循环和分支结构为脚本编程提供了操作代码块的工具,流程控制主要包括测试条件、循环控制语句、分支控制语句及与其相关的操作控制符。

条件测试

bash由test命令、各种括号和内嵌的操作符,以及if/then结构来完成条件测试的功能。通常用“[  ]”表示条件测试。注意,这里的空格很重要,要确保方括号两侧的空格。

比较操作符

比较操作包括整数比较操作、字符串比较操作和混合比较操作。常用的操作符如下表所示:

常用整数比较操作符
函数 说明(例子)
-eq 等于,if [ "$a" -eq "$b" ]
-ne 不等于,if [ "$a" -ne "$b" ]
-gt 大于,if [ "$a" -gt "$b" ]
-ge 大于等于,if [ "$a" -ge "$b" ]
-lt 小于,if [ "$a" -lt "$b" ]
-le 小于等于,if [ "$a" -le "$b" ]
< 小于(在双括号内使用),(("$a"<"$b"))
<= 小于等于(在双括号内使用),(("$a"<="$b"))
> 大于(在双括号内使用),(("$a">"$b"))
>= 大于等于(在双括号内使用),(("$a">="$b"))
常用字符串比较操作符
函数 说明(例子)
= 等于,if [ "$a"="$b" ]
== 等于,if [ "$a"=="$b" ],在[[  ]]结构里使用匹配模式
!= 不等于,if [ "$a"!="$b" ],在[[  ]]结构里使用匹配模式
< 小于,if [[ "$a"<"$b" ]],if [ "$a"\<"$b" ](在[]结构里<需要转义)
> 大于,if [[ "$a">"$b" ]],if [ "$a"\>"$b" ](在[]结构里>需要转义)
-z 字符串为“null”,长度为0
-n 字符串不为“null”,长度不为0
常用混合比较操作符
函数 说明
-a 逻辑与
-o 逻辑或
文件测试操作符
常用文件测试操作符
函数 说明
-e 文件存在
-f 文件是一个普通文件(不是目录或设备文件)
-s 文件大小不为零
-d 文件是一个目录
-b 文件是一个块设备(如光盘、光驱)
-c 文件是一个字符设备(如键盘、调制解调器、声卡等)
-p 文件是一个管道
-h 文件是一个符号链接
-L 文件是一个符号链接
-S 文件是一个Socket
-t 文件与一个终端设备相关
-r 文件是否可读
-w 文件是否可写
-x 文件是否可执行
-g 文件或目录的sgid标志被设置
-u 文件的sgid标志被设置

分析下列的测试目录含义

[[ $a==z* ]]                #如果变量$a以字符"z"开始(匹配模式)则为真
[[ $a=="z*" ]]                #如果变量$a与字符"z*"(字面上的匹配)相等则为真
[ "$a"=="z*" ]                #如果变量$a与字符"z*"(字面上的匹配)相等则为真
[ -f "somefile" ]                #判断是否为一个文件
[ -x "/bin/ls" ]                #判断/bin/ls是否有可执行的权限
[ -n "$var" ]                #判断$var变量是否有值
[ "$a"="$b" ]                #判断$a和$b是否相等

嵌套的if/then语句

if [ condition1 ]
    then
        if [ condition2 ]
            then
            do-something            #仅当condition1和condition2同时满足才执行
        fi
fi

需要注意的是:

  • [ ]表示条件测试。注意这里的空格很重要。要注意在'['后面和']'前面都必须要有空格;
  • 在shell中,then和fi是分开的语句。如果要在同一行里面输入,则需要用分号将他们隔开。

如果还有什么不理解的,可以参考链接:Shell脚本IF条件判断和判断条件总结

操作符相关主题

常见的操作符主要包括赋值操作符、计算操作符、位操作符合逻辑操作符。它们与C语言的操作符是相同的。包括:

  • 赋值操作符:=;
  • 计算操作符:+、-、*、/、**(求幂)、%;
  • 位操作符:<<、<<=、>>、>>=、&、&=、|、|=、~、!、^、^=;
  • 逻辑运算符:&&、||。

循环控制

for循环
for arg in [list]
do
    do-something
done

其中,list中的参数允许包括通配符。如果do和for想在同一行出现,那么在它们之间需要添加一个“;”。

while循环

while结构再循环的开始判断条件是否满足,如果条件一直满足,那就一直循环下去(0为退出码),与for循环的区别是,while结构适合在循环次数未知的情况下。while循环的结构如下:

while [condition]
do
    do-something
done

和for循环一样,如果想把do和条件放到同一行,需要一个“;”。

#!/bin/bash
var0=0
LIMIT=10
while [ "$var0" -lt "$LIMIT" ]
do
    echo -n "$var0"        //-n将会阻止产生新行
    let "var0+=1"        //let语句用于指定算术运算
done
exit 0

打开终端,建立该脚本文件,运行结果如下:

[root@localhost ~]# ./test1
0 1 2 3 4 5 6 7 8 9
until循环

until结构在循环的顶层判断条件,如果条件一直为false,那就一直循环下去(与while相反)。until循环的结构如下:

until [condition-is-true]
do
    do-something
done

和for循环一样,如果想把do和条件放到同一行,需要一个“;”。

#!/bin/bash
END_CONDITION=end
until [ "$var1"="$END_CONDITION" ]
do
    echo "Input varible"
    echo "($END_CONDITION to exit)"
    read var1                        //提示用户输入,并将输入值赋值给变量
    echo "varible=$var1"
done
exit()

打开终端,建立该脚本文件,运行结果如下:

[root@localhost ~]# ./test1
Input varible
(end to exit)
3
varible=3
Input varible
(end to exit)
end
varible=end
break和continue

break和continue这两个循环控制命令与其他语言的类似命令的行为是相同的,break命令将会跳出循环,continue命令会跳过本次循环后面的语句,直接进入下次循环。

  • break命令可以带一个参数,不带参数的break循环只能退出最内层的循环,而break N则可以退出N层循环;
  • continue命令可以带一个参数,不带参数的continue命令只能跳过本次循环,而continue N则可以把N层循环剩余的代码都跳过,但是循环的次数不变。

测试与分支

case和select结构在技术上不是循环,因为它们并不对可执行的代码块进行迭代。但是和循环相似的是,它们也依靠在代码块的顶部或底部的条件判断来决定程序的分支。

shell中的case同C/C++中的switch结构是相同的,它允许通过条件来选择执行代码块中多条路径中的一条。它的作用和多个if/then/else语句相同,是它们的简化结构,特别适合于创建目录。

猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80514739