构建脚本
10.1 使用多个命令
shell脚本的关键在于输入多个命令,并处理命令的结果,即使有可能将一个命令的结果传给另一个命令。
如果两个命令一起运行,可在同一提示行输入它们,用分号分隔开。
- 命令列表:在命令行中输入 date;who 命令
- 进程列表:在命令行中输入(date;who) 命令
[root@centos1 ~]# date ; who Sat Dec 9 10:39:17 CST 2017 root pts/0 2017-12-09 10:01 (192.168.20.224)
10.2 创建shell脚本文件
要将shell命令放到一个文本文件中。在创建shell脚本文件时,必须在文件的第一行制动要使用的shell 其格式为:
#!/bin/bash
在通常的shell脚本里, 井号 # 用作注释行。 shell脚本 注释行是不被shell执行的。然而,shell脚本文件的第一行是个特例,井号后接叹号 告诉shell用哪个shell来运行脚本。
在指定了shell之后可在文件的每行输入命令,然后加一个回车符。之前提到过,注释可用井号添加 。
以# 号 开头的行都不会被shell处理 出了以(#!开头的第一行) 。
chmod u+x abc
10.3 显示消息
在bash脚本中可以使用echo 命令打印输出:
[root@centos1 ~]# echo This is a test This is a test
如果在字符串中出现引号的话就可能比较麻烦,echo 命令可用单引号或使用双引号来将文本字符串圈起来。如果你再文本中使用一种引号,二用另外一种来将字符串圈起来:
[root@centos1 ~]# echo "Lets's go" Lets's go
[root@centos1 ~]# cat abc #!/bin/bash echo "The time and date are:" date; who [root@centos1 ~]# ./abc The time and date are: Sat Dec 9 11:05:23 CST 2017 root pts/0 2017-12-09 10:01 (192.168.20.224)
如果想在同一行显示一个文本字符串作为命令输出:可以使用echo -n 参数,只要将第一个echo语句改为这样就行。
[root@centos1 ~]# cat abc #!/bin/bash echo -n "The time and date are:" date; who [root@centos1 ~]# ./abc The time and date are:Sat Dec 9 11:07:16 CST 2017 root pts/0 2017-12-09 10:01 (192.168.20.224)
完美! echo 命令是shell脚本中桶用户交互的重要工具。
10.4.1 环境变量
[root@centos1 bash_dir]# cat test2 #!/bin/bash echo "User info for userid : $USER" echo UID : $UID echo HOME : $HOME [root@centos1 bash_dir]# ./test2 User info for userid : root UID : 0 HOME : /root
注意,echo命令中的环境变量会在银杏是替换成当前值。还有在第一个字符串中我们可以将$USER系统变量放置到双引号中,而shell依然能够知道我们的意图。但采用这种方式也有一个问题,看看下面这个例子会怎样。
[root@centos1 bash_dir]# echo "The $15" The 5
显然这不是我们想要的,要显示美元符,你必须在前面防止一个反斜线:
[root@centos1 bash_dir]# echo "The \$15" The $15
反斜线允许shell脚本将美元符解释为实际的美元符,而不是变量。下一节将会介绍如何在脚本中创建自己的变量。
你可能还见过${variable} 形式引用的变量。变量名两侧额外的花括号通常用来帮助识别美元符后的变量名。
10.4.2 用户变量
除了环境变量,shell脚本还允许在脚本定义和使用自己的变量,定义变量允许临时存储数据并在整个脚本中使用,从而使shell脚本看起来更像计算机程序。
用户变量可以是任何不超过20个字母,数字或 下划线的文本字符串。 用户变量区分大小写,所以变量Var1和变量var1 是不同的。
值通过等号赋给用户变量。在变量、等号和值之间不能出现空格
- var1=10
- var2=-57
- var3=testing
- var4="Still more testing"
shell脚本会自动决定变量值的数据类型。在shell脚本的整个生命周期里,shell脚本中定义的变量会一致保持着它们的值,但在shell脚本完成时删除掉。
[root@centos1 bash_dir]# cat test3 #!/bin/bash days=10 guest="Katie" echo $guest "checked in $days" [root@centos1 bash_dir]# ./test3 Katie checked in 10
10.4.3 反引号
shell脚本中最有用的特性之一就是反引号(`) .在shell脚本之外很少用到它,反引号允许你将shell命令的输出赋给变量。
你必须用反引号把这个命令行命令圈起来:
[root@centos1 bash_dir]# testing=`date`; echo $testing Sat Dec 9 11:37:43 CST 2017 [root@centos1 bash_dir]# echo $testing Sat Dec 9 11:37:43 CST 2017 [root@centos1 bash_dir]# (test=`date`; echo $test) Sat Dec 9 11:38:31 CST 2017 [root@centos1 bash_dir]# echo $test [root@centos1 bash_dir]#
[root@centos1 bash_dir]# cat var1 #!/bin/bash today=`date +%y%m%d` ls /usr/bin -al > log.$today [root@centos1 bash_dir]# ls abc log.171209 test2 test3 var1
10.5 重定向输入和输出
有些时候你想要保存某个命令的输出而非在显示器上显示它。 bash shell提供了一些不同的操作符来将某个命令的输出重定向到另一个位置(比如文件)。重定向可以通过将某个文件重定向到某个命令上来用在输入上, 也可以用在输出上。
10.5.1 输出重定向
重定向最基本的类型是将命令的输出发到一个文件中。 bash shell采用大于号(>) 来完成这个功能
command > outputfile
[root@centos1 bash_dir]# date > test3 [root@centos1 bash_dir]# cat test3 Sat Dec 9 11:51:58 CST 2017
重定向操作符创建了一个文件test3(通过默认的umusk设置), 如果输出文件已存在了,则这个重定向操作符会用新的文件数据覆盖已存在的文件。
有时需要将命令的输出追加到已有文件上 可以用双大于号(>>) 来追加数据:如果文件不存在则创建。
date >> test4
10.5.2 输入重定向
输入重定向和输出重定向正好相反。输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。
输入重定向符号是小于号(<) :
command < inputfile
[root@centos1 bash_dir]# wc test2 4 16 82 test2 [root@centos1 bash_dir]# wc < test2 4 16 82
wc 命令提供了对数据中文本的计数。 默认情况下,它会输出3个值:
- 文本行数
- 文本词数
- 文本的字节数
还有一种输入重定向的方法,成为内联输入重定向。 这种方法允许你在命令行,而不是在文件指定输入重定向的数据。内联输入重定向符号是双小于号(<<)。除了这个符号,你必须指定一个文本标记来划分要输入数据的开始和结尾。 你可以用任何字符串的值来作为文本标记。但在数据的开始和结尾必须一致:
command << marker
data
marker
在这个命令行上使用内敛输入重定向时,shell会用PS2 环境变量中定义的次提示符,来提示输入数据。下面是使用它的情况:
[root@centos1 bash_dir]# wc << EOF > aksjfd > kajsdl > lakjsdlfl > askdfl > lskfks > s > lksdf > EOF 7 7 46 [root@centos1 bash_dir]# wc << aa > ksjdfk > ksdjfk > iwerui > aa 3 3 21
次提示符会一直提示输入更多的数据,知道你输入了作为文本标记的那个字符串值。wc 命令会对内联输入重定向提供的数据执行 行 词 和字节计数。
10.6 管道
有时你需要发送某个命令的输出作为另一个命令的输入。可以用重定向,会有些笨拙
rpm -qa > rpm.list sort < rpm.list
先输出linux系统上安装文件的列表到rpm.list文件中。
类似于反引号(`) 管道的符号在 shell编程之外也很少用到。管道放在命令键,将一个命令的输出重定向到另一个上: command1 | command2
不要以为管道连接会一个一个地运行,Linux系统实际上会同时运行这两个命令,在系统内部将它们连接起来。在第一个命令产生输出的同时,输出会被立即送到第二个命令。传输数据不会用到任何中间文件或缓冲区。
[root@centos1 bash_dir]# rpm -qa | sort
可以在一条命令中使用任意多条管道,可以继续将命令的输入通过管道传给其他命令来简化操作。
[root@centos1 bash_dir]# rpm -qa | sort | more
10.7 执行数学运算
在shell脚本中有两种痛经进行数学运算操作
10.7.1 expr命令
可以处理数学表达式,但是特别笨拙。
var1=`expr 1/2`
10.7.2 使用方括号
bash shell 为了保持跟Boume shell的兼容而包含了expr命令。但它同样也提供了一个执行数学表达式的更简单的方法。 bash中 将一个数学运算结果赋给某个变量时,你可以用美元符和 方括号($[operation]) 将数学表达式圈起来:
[root@centos1 bash_dir]# (var=$[1 + 5];echo $var) 6 [root@centos1 bash_dir]# (var=$[1 + 5];echo $var ; var2=$[$var * 2]; echo $var2) 6 12
同样,注意在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号,shell知道它不是通配符,因为它在方括号内。
bash shell数学运算只支持整数,如果要进行任何数学运算,这是一个巨大的限制。
10.7.3 浮点解决方案
使用内建的bash计算器,称作bc。
1.bc的基本用法
bash计算器其实是允许在命令行输入浮点表达式、解释表达式、计算并返回结果的一种编程语言。bash计算器能够识别:
- 数字(整数和浮点数)
- 变量(简单变量和数组)
- 注释(以# 开始IDE行或 C语言中的/* */对)
- 表达式
- 编程语句(例如if-then语句)
- 函数
你可以在shell提示符下通过bc命令访问bash 计算器:
[root@centos1 bash_dir]# bc bc 1.06.95 Copyright 1991-1994,1997,1998,2000,2004,2006 Free Software Foundation,Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. 12 * 54 648 12 * 5.4 64.8 3.156 * (3+5) 25.248 quit
浮点运算是由一个内建的成为scale的变量控制的。你必须将这个字设置为结果里你想要的小数后的位数,否则你不会得到想要的结果的:
[root@centos1 bash_dir]# bc -q 3.44/5 0 scale=4 3.44/5 .6880 quit [root@centos1 bash_dir]#
scale变量默认值是0 , 在scale值被设置前,bash计算器提供的答案没有小数点后的位置。在将其设置为4之后,bash计算器显示的答案有4位小数。-q命令行参数会将bash计算器输出的很长的欢迎条屏蔽掉。
出了普通数字,bash计算器还能支持变量:
[root@centos1 bash_dir]# bc -q var1=10 var*4 0 var1*4 40 var2=var1/5 print var2 2 quit [root@centos1 bash_dir]#
2.在脚本中使用bc
可以使用反引号来运行bc命令并将输出赋给一个变量。
variable=`echo "options;expression" | bc`
[root@centos1 bash_dir]# (variable=`echo "scale=4;3.44/5" | bc`; echo $variable) .6880
这里有个在脚本中使用它的例子:
[root@centos1 bash_dir]# cat calculate #!/bin/bash var1=`echo "scale=4;3.44/5" | bc` echo "The answer is $var1" [root@centos1 bash_dir]# ./calculate The answer is .6880 [root@centos1 bash_dir]#
这个例子在scale变量设置成了四位小苏,并未表达式指定了特定的运算。
[root@centos1 bash_dir]# cat varcalcu #!/bin/bash var1=100 var2=45 var3=`echo "scale=4;$var1/$var2" | bc` echo "The answer for this is $var3" [root@centos1 bash_dir]# ./varcalcu The answer for this is 2.2222 [root@centos1 bash_dir]#
脚本中定义了两个变量,它们都可以用在表达式中发送给bc命令。使用美元符来表示变量的值而不是变量自身,这个脚本输出如下:
这个方法适用于较短的运算,但有时你会更多地和你自己的数字打交道。如果你有很多运算,在同一个命令行中列出多个表达式就会有点麻烦。
针对这个问题有个解决办法。bc命令能够识别输入重定向,允许你将一个文件重定向到bc命令来处理。然而这样也叫人困惑,因为你必须将表达式存储到文件中。
最好的办法是使用内联输入重定向,允许你直接在控制台重定向数据。在shell脚本中,你可以将输出赋给一个变量:
variable=`bc << EOF options statements expressions EOF `
EOF文本 字符串标识了内联重定向数据的开始和结尾。记住仍然需要反引号来将bc命令的输出赋给变量。
[root@centos1 bash_dir]# cat eof #!/bin/bash var1=10.46 var2=43.67 var3=33.2 var4=71 var5=`bc << EOF scale=4 a1=$var1*$var2 b1=$var3*var4 a1+bc EOF ` echo "The final answer for this mess is $var5" [root@centos1 bash_dir]# ./eof The final answer for this mess is 456.7882
将每个选项和表达式放在脚本中不同的行中,可以让事情变得更清晰,也更容易阅读和跟进。EOF字符串标识了内联重定向给bc命令数据的起始和结尾。当然,你也要用反引号来标识输出要赋给变量的命令。
bash运算器中创建的变量只在bash计算器中有效,不能再shell脚本中使用。
10.8 退出脚本
我们的实例脚本那种总是匆忙结尾,在运行完最后一条命令时,就结束了脚本。这里还有一个更好的结束事情的办法。
shell中运行的每个命令都是使用退出状态码(exit status) 来告诉shell它完成了处理。退出状态码是一个0~255 之间的整数值,在命令结束运行时由命令传给shell,你可以捕获这个值并在脚本中使用。
10.8.1 查看退出状态码
Linux提供了 $? 专属变量来保存上个执行的命令的退出状态码。必须在要查看的命令之后马上查看或使用$?变量。它的值会变成shell中执行的最后一条命令的退出状态码:
[root@centos1 bash_dir]# date Sat Dec 9 14:57:41 CST 2017 [root@centos1 bash_dir]# echo $? 0 [root@centos1 bash_dir]#
[root@centos1 bash_dir]# sdkjf -bash: sdkjf: command not found [root@centos1 bash_dir]# echo $? 127 [root@centos1 bash_dir]#
无效命令会返回一个退出状态码127 。Linux错误退出状态码没有什么标准惯例。但有一些可用的参考,如下
[root@centos1 bash_dir]# date %t date: invalid date `%t' [root@centos1 bash_dir]# echo $? 1 [root@centos1 bash_dir]#
10.8.2 exit命令
默认情况下,shell脚本会以脚本中的最优一个命令的退出状态码退出:
[root@centos1 bash_dir]# ./eof The final answer for this mess is 456.7882 [root@centos1 bash_dir]# echo $? 0 [root@centos1 bash_dir]#
你可以改变这个来返回你自己的退出状态码。exit命令允许你再脚本结束时指定一个退出状态码:
[root@centos1 bash_dir]# cat test10 #!/bin/bash var1=20 var2=10 var3=`bc << EOF $var1+var2 EOF ` echo "The answer is $var3" exit 5 [root@centos1 bash_dir]# ./test10 The answer is 20 [root@centos1 bash_dir]# echo $? 5
也可以在exit命令的参数中使用变量
exit $var
需注意使用exit时 参数值最大只能255 。 结果值是指定的数被256除的余数。 如果参数为300 余数则为44,它就成了最后的状态退出码。