Shell脚本学习笔记(一)

第一章 构建基本脚本

1.1 使用多个命令

shell脚本的关键在于输入多个命令并处理每个命令的结果,甚至将一个命令的结果传给另一个命令。shell可以让你将多个命令串起来,一次执行完成。如果要两个命令一起运行,可以把他们放在同一行中,彼此用分号隔开。

$ date ; who
Mon Feb 21 15:36:09 EST 2014
Christine tty2 2014-02-21 15:26
Samantha tty3 2014-02-21 15:26

这种在命令行执行的方式对于小型脚本尚可,但有一个致命的缺陷,每次运行之前,你都必须在命令提示符下输入整个命令。可以将这些命令组合成一个简单的文本文件,需要运行这些命令时,只用运行这个文本文件就好了。

1.2 创建shell脚本文件

在创建shell脚本文件时,必须在文件的第一行指定要使用的shell,其格式为:

#!/bin/bash

在通常的shell脚本中,#用作注释行。shell并不会处理shell脚本中的注释行。然而shell脚本文件中的第一行是一个例外,#后面的!会告诉shell用哪个shell来运行脚本。

#!/bin/bash
# This script displays the date and who's logged on
date
who

可以根据需要,使用;将两个命令放在一行,也可以将命令写在独立的行中,shell会按照命令在文件中出现的顺序进行处理。上例中#开头的行内容为注释,shell不会解释以#开头的行(第一行#!除外),留下来注释说明脚本做什么是非常好的习惯。

在将shell脚本文件保存后,就可以尝试运行脚本了。运行前需要做如下之一:

shell脚本文件所在的目录添加到PATH环境变量中

在命令行提示符中用绝对或相对文件路径来引用shell脚本文件

$ ./test1
bash: ./test1: Permission denied

提示没有执行权限,使用ls -l命令查看shell脚本文件权限:

$ ls -l test1
-rw-rw-r-- 1 user user 73 Sep 24 19:56 test1

使用chmod命令为shell脚本添加执行权限:

$ chmod u+x test1
$ ./test1
Mon Feb 21 15:38:19 EST 2014
Christine tty2 2014-02-21 15:26
Samantha tty3 2014-02-21 15:26
1.3 显示消息

echo命令用来显示消息。

$ echo This is a test
This is a test

默认情况,不需要使用引号将要显示的文本字符串划定出来。如果字符串中出现引号可能无法输出。

$ echo Let's see if this'll work
Lets see if thisll work

echo命令可以用'"来划定文本字符串,如果在字符串中用到'",需要在文本中使用其中一种引号,另外一种将字符串划分开。

$ echo "This is a test to see if you're paying attention"
This is a test to see if you're paying attention
$ echo 'Rich says "scripting is easy".'
Rich says "scripting is easy".

可以将echo语句添加到shell脚本中任何需要显示额外的信息的地方。

$ cat test1
#!/bin/bash
# This script displays the date and who's logged on
echo The time and date are:
date
echo "Let's see who's logged into the system:"
who

运行脚本:

$ ./test1
The time and date are:
Mon Feb 21 15:41:13 EST 2014
Let's see who's logged into the system:
Christine tty2 2014-02-21 15:26
Samantha tty3 2014-02-21 15:26

使用echo -n可以将文本字符串和命令行输出显示在同一行中。

$ ./test1
The time and date are: Mon Feb 21 15:42:23 EST 2014
Let's see who's logged into the system:
Christine tty2 2014-02-21 15:26
Samantha tty3 2014-02-21 15:26

echo命令是shell脚本与用户交互的重要工具。

1.4 使用变量

环境变量

shell维护着一组环境变量,用来记录特定的系统信息。比如系统名称,登录系统的用户名,用户系统ID,用户的默认主目录以及shell查找程序的搜索路径。可以用set命令来显示一份完整的当前环境变量列表。

$ set
BASH=/bin/bash
[...]
HOME=/home/Samantha
HOSTNAME=localhost.localdomain
HOSTTYPE=i386
IFS=$' \t\n'
IMSETTINGS_INTEGRATE_DESKTOP=yes
IMSETTINGS_MODULE=none
LANG=en_US.utf8
LESSOPEN='|/usr/bin/lesspipe.sh %s'
LINES=24
LOGNAME=Samantha

在脚本中,可以环境变量名称前加上美元符$来使用这些环境变量。$USER,$UID和$HOME环境变量用来显示已登录用户的有关信息。echo命令中的环境变量会在脚本运行时替换成当前值。还可以将$USER系统变量放置到双引号中,而shell能够正确识别环境变量。

$ cat test2
#!/bin/bash
# display user information from the system.
echo "User info for userid: $USER"
echo UID: $UID
echo HOME: $HOME
$chmod u+x test2
$ ./test2
User info for userid: Samantha
UID: 1001
HOME: /home/Samantha
$

只要在脚本中出现美元符号,shell就会认为在引用一个变量。由于$1并没有被定义,所以不会显示。如果要显示美元符号,必须在它前面放置一个反斜线。

$ echo "The cost of the item is $15"
The cost of the item is 5

$ echo "The cost of the item is \$15"
The cost of the item is $15

用户变量

除了环境变量,shell脚本还允许在脚本中定义和使用自己的变量。定义变量允许临时存储数据并在整个脚本中使用。用户变量可以是任何字母,数字或下划线组成的文本字符串,长度不超过20,用户变量区分大小写。使用等号将值赋给用户变量,在变量,等号和值之间不能出现空格。

var1=10
var2=-57
var3=testing
var4="still more testing"

shell脚本会自动决定变量值的数据类型。在脚本的整个生命周期里,shell脚本中定义的变量会一直保持着他们的值,但在shell脚本结束时会被删除掉。与系统变量类似,用户变量可通过美元符引用。

$ cat test3
#!/bin/bash
# testing variables
days=10
guest="Katie"
echo "$guest checked in $days days ago"
days=5
guest="Jessica"
echo "$guest checked in $days days ago"
$ chmod u+x test3
$ ./test3
Katie checked in 10 days ago
Jessica checked in 5 days ago

变量每次被引用时,都会输出当前赋给它的值。引用一个变量时需要使用美元符,而引用变量来对其进行赋值时则不使用美元符。

命令替换

可以从命令输出中提取信息,并将其赋给变量。有两种方法可以将命令输出赋给变量反引号字符(`)和$()格式。

testing=`date`
testing=$(date)

shell会运行命令替换符号中的命令,并将其输出赋给变量testing。

#!/bin/bash
# copy the /usr/bin directory listing to a log file
today=$(date +%y%m%d)
ls /usr/bin -al > log.$today

$ date +%y%m%d
140131

today变量是被赋予格式化后的date命令的输出。这是提取日期信息来生成日志名常用的技术。+%y%m%d格式告诉date命令将日期显示为两位数的年月日组合。命令替换会创建一个子shell来运行对应的命令,子shell是由运行该脚本的shell所创建出来的一个独立的子shell,所以由该子shell所执行命令是无法使用脚本中所创建的变量的。在命令行提示符使用./运行命令的话,也会创建出子shell,要是运行命令的时候不加入路径,就不会创建子shell。

1.5 重定向输入和输出

如果想保存某个命令的输出而不仅仅只是让他显示在显示器上,bash shell提供了几个操作符,可以将命令的输出重定向到另一个位置(比如文件)。重定向可以用于输出,可以将文件重定向到命令输入。

输出重定向

最基本的重定向将命令的输出发送到一个文件中。

command > outputfile
$ date > test6
$ ls -l test6
-rw-r--r-- 1 user user 29 Feb 10 17:56 test6
$ cat test6
Thu Feb 10 17:56:58 EDT 2014

重定向操作符创建了一个文件test6,并将date命令的输出重定向到该文件中。如果输出文件已经存在了,重定向操作符会用新的文件数据覆盖已有的文件。

>>符号不会覆盖文件原有内容,而是将命令的输出追加到已有文件中。

$ date >> test6
$ cat test6
user pts/0 Feb 10 17:55
Thu Feb 10 18:02:14 EDT 2014

输入重定向

输入重定向和输出重定向正好相反。输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。

command < inputfile

$ wc < test6
2 11 60

在命令行上,命令总是在左侧,而重定向符号指向数据流动的方向。wc命令可以对数据中的文本进行技术。默认情况下他会输出3个值:文本的行数,文本的词数,文本的字节数。

另一种输入重定向的方法称为内联输入重定向(inline input redirection),这种方法无需使用文件进行重定向,只需要在命令行中指定用于输入重定向的数据就可以了。内联输入重定向符号是<<符号。除此之外,必须指定一个文本标记来划分输入数据的开始和结尾。任何字符串都可以作为文本标记,但在数据的开始和结束文本标记必须一致。

$ wc << EOF
> test string 1
> test string 2
> test string 3
> EOF
3 9 42
$
1.6 管道

有时候需要将一个命令的输出作为另一个命令的输入。如使用重定向实现,有些繁琐,可以使用管道连接(piping)。管道符号为|,命令格式为:

command1 | command2

不要以为管道串联的两个命令会依次执行。Linux系统会同时运行这两个命令,在系统内部将它们连接起来。在第一个命令产生输出的同时,输出会被立即送给第二个命令。数据传输不会用到任何中间文件或缓冲区。以下命令输出的结果是相同的:

$ rpm -qa > rpm.list
$ sort < rpm.list
abrt-1.1.14-1.fc14.i686
abrt-addon-ccpp-1.1.14-1.fc14.i686
abrt-addon-kerneloops-1.1.14-1.fc14.i686

$ rpm -qa | sort
abrt-1.1.14-1.fc14.i686
abrt-addon-ccpp-1.1.14-1.fc14.i686
abrt-addon-kerneloops-1.1.14-1.fc14.i686

可以在一条命令中使用任意多条管道,在上条命令中可以使用分页命令来强行按屏显示。

$ rpm -qa | sort | more

也可以将输出保存到文件中。

$ rpm -qa | sort > rpm.list
1.7 执行数学运算

另一个对任何编程语言都很重要的特性是操作数字的能力。对shell脚本来说,这个处理过程比较麻烦。通常shell脚本有两种途径来进行数学运算。

expr命令允许在命令行上处理数学表达式,但是特别笨拙。

$ expr 1 + 5
6

使用方括号

在bash中,将一个数学运算结果赋给某个变量时,可以用美元符号和方括号($[ operation ])将数学表达式围起来。

$ var1=$[1 + 5]
$ echo $var1
6
$ var2=$[$var1 * 2]
$ echo $var2
12

用方括号执行shell数学运算符比用expr命令方便很多,这种技术也适用于shell脚本。

$ cat test7
#!/bin/bash
var1=100
var2=50
var3=45
var4=$[$var1 * ($var2 - $var3)]
echo The final result is $var4
$
$ chmod u+x test7
$ ./test7
The final result is 500
$

在使用方括号来计算公式时,不用担心shell会误解乘号或其他符号,shell知道它不是通配符,因为在方括号中。在bash shell脚本中进行算术运算的限制是bash shell只支持整数运算。

浮点数运算解决方案

最常见的使用内建bash计算器,叫做bc。bc计算器实际上是一种编程语言,允许在命令行中输入浮点表达式,然后解释并计算该表达式,最后返回结果。bash能够识别:

整数(整数和浮点数)

变量(简单变量和数组)

注释(以#,或者/* */开始的行)

表达式

编程语句(例如if-then语句)

函数

$ 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 * 5.4
64.8
3.156 * (3 + 5)
25.248
quit
$

这个例子一开始输入了表达式12 * 5.4,bash计算器返回了计算结果。随后每个输入到计算器的表达式都会被求值并显示出结果。要退出bc计算器,必须输入quit。

浮点运算使由内建变量scale控制的,scale变量的默认值是0,必须将这个值设置为你希望在计算结果中保留的小数位数,否则无法得到期望的结果。

$ bc -q
3.44 / 5
0
scale=4
3.44 / 5
.6880
quit
$

除了普通字符,bc还支持变量。

$ bc -q
var1=10
var1 * 4
40
var2 = var1 / 5
print var2
2
quit
$

在脚本中使用bc

可以用命令替换运行bc命令,并将输出赋给一个变量,基本格式如下:

variable=$(echo "options; expression" | bc)

option允许你设置变量,如果需要不止一个变量,可以用分号将其分开。expression参数定义了通过bc执行的数学表达式。

$ cat test9
#!/bin/bash
var1=$(echo "scale=4; 3.44 / 5" | bc)
echo The answer is $var1

$ chmod u+x test9
$ ./test9
The answer is .6880

也可以使用shell脚本中定义好的变量。

$ cat test10
#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc)
echo The answer for this is $var3

$ ./test10
The answer for this is 2.2222

脚本定义了两个变量,他们都可以在expression使用,然后发送给bc命令。

以上方法适用于较短的数学运算,如果需要进行大量运算,在一个命令行中列出多个表达式就会很麻烦。bc命令能够识输入重定向,允许将一个文件重定向到bc命令来处理。最好的办法是使用内联重定向,他会允许你在命令行中重定向数据。

variable=$(bc << EOF
options
statements
expressions
EOF
)

$ cat test12
#!/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 + b1
EOF
)
echo The final answer for this mess is $var5

EOF字符串标识了重定向给bc命令的数据的起止。

1.8 退出脚本

shell中运行的每个命令都使用退出状态码(exit status)告诉shell已经运行完毕。退出状态码是一个0~255的整数值,在命令结束运行时由命令传给shell,可以捕获这个值并在脚本中使用。

查看退出状态码

Linux提供了一个专门的变量$?来保存上个已执行命令的退出状态码。对于需要进行检查的命令,必须在其运行完毕后立刻查看或者使用$?变量。他的值会变成由shell所执行的最后一条命令的退出状态码。

$ date
Sat Jan 15 10:01:30 EDT 2014
$ echo $?
0
$

按照惯例,一个成功结束的命令的退出状态码是0,如果一个命令结束时有错误,退出状态码是一个正数。

$ asdfg
-bash: asdfg: command not found
$ echo $?
127

无效命令会返回一个退出状态码127。常见的状态码含义如下:

状态码 描述
0 命令成功结束
1 一般性未知错误(给命令提供了无效参数会报此错)
2 不合适的shell命令
126 命令不可执行(表示用户没有执行命令的权限)
127 没找到命令
128 无效的退出参数
128+x 与Linux信号x相关的严重错误
130 通过Ctrl+C终止的命令
255 正常范围之外的退出状态码

exit命令

默认情况,shell脚本会以脚本中的最后一个命令的退出状态码退出。可以修改默认行为,返回自己的退出状态码。exit命令允许你在脚本结束时指定一个退出状态码。

$ cat test13
#!/bin/bash
# testing the exit status
var1=10
var2=30
var3=$[$var1 + $var2]
echo The answer is $var3
exit 5

$ chmod u+x test13
$ ./test13
The answer is 40
$ echo $?
5

也可以在exit命令的参数中使用变量。

$ cat test14
#!/bin/bash
# testing the exit status
var1=10
var2=30
var3=$[$var1 + $var2]
exit $var3

$ chmod u+x test14
$ ./test14
$ echo $?
40

退出状态码最大只能是255,如果退出状态码大于255,shell通过模运算(除以256的余数)得到新的结果。

$ cat test14b
#!/bin/bash
# testing the exit status
var1=10
var2=30
var3=$[$var1 * $var2]
echo The value is $var3
exit $var3
$

$ ./test14b
The value is 300
$ echo $?
44
$

本章小结

​ 1.介绍了基本的shell脚本

​ 2.显示命令echo

​ 3.系统变量与用户变量

​ 4.输入/输出重定向

​ 5.管道操作符

​ 6.bash shell中的数学运算

​ 7.退出shell脚本与退出状态码

参考文献:Linux命令行与shell脚本编程大全(第三版)Richard Blum Christine Bresnahan著 门佳 武海峰译

你可能感兴趣的:(shell脚本,学习,bash,linux)