第11章:构建基本脚本
[TOC]
1. 使用多个命令
shell脚本的关键在于输入多个命令并处理每个命令的结果,甚至需要将一个命令的结果传给另一个命令。shell可以将多个命令串起来,一次执行完成。如果要两个命令一起运行,可以把它们放在同一行中,彼此用分号隔开。
$ date;who
2019年 09月 19日 星期四 19:59:03 CST
henryhu tty7 2019-09-19 10:49 (:0)
这个简单的脚本只用到了两个bash shell命令。date命令先运行,显示了当前日期和时间,后面紧跟着who命令的输出,显示当前是谁登陆到了系统上。
2. 创建shell脚本文件
要将shell命令放到文本文件中,首先需要用文本编辑器来创建一个文件,然后将命令输入到文件中。
在创建shell脚本文件时,必须在文件的第一行指定要使用的shell。其格式为:
#!/bin/bash
在通常的shell脚本中,# 用作注释行。shell并不会处理shell脚本中的注释行。然而,shell脚本文件的第一行是个例外,# 后面的 ! 会告诉shell用哪个shell来运行脚本(你可以使用bash shell,同时还可以使用另一个shell来运行你的脚本)。
在指定了shell之后,就可以在文件的每一行中输入命令,然后加一个回车符。
#!/bin/bash
# This script displays the date and who's logged on
date
who
可以根据需要,使用分号(;)将两个命令放在一行上,但在shell脚本中,你可以在独立的行中书写命令。shell会根据命令在文件中出现的顺序进行处理。
将这个脚本保存在名为test1的文件中,基本就好了。在运行新脚本前,还要做其他一些事。现在运行脚本会出现以下情况:
$ test1
No command 'test1' found, did you mean:
Command 'test' from package 'coreutils' (main)
Command 'testr' from package 'python-testrepository' (universe)
Command 'testr' from package 'python3-testrepository' (main)
test1: command not found
需要解决的第一个问题就是让bash shell能找到你的脚本文件。要让shell找到test1脚本,只需要采取以下两种方式之一:
- 将shell脚本文件所在的目录添加到PATH环境变量;
- 在提示符中用绝对或相对文件路径来引用shell脚本文件。
在这个例子中,我们将用第二种方式将脚本文件的确切位置告诉shell。记住,为了引用当前目录下的文件,可以在shell中使用单点操作符。
$ ./test1
bash: ./test1.sh: Permission denied
现在shell找到了脚本文件,但还有一个问题。shell指明了你还没有执行文件的权限。快速查找看一下文件权限就能找到问题所在。
$ ls -l test1.sh
-rw-r--r-- 1 root root 73 9月 19 20:16 test1.sh
在创建test1文件时,umask的值决定了新文件的默认权限设置。由于unmask变量在Ubuntu中被设成了022,所以系统创建的文件只有文件属主和属组才有读/写权限。
下一步是通过chmod命令赋予文件属主执行文件的权限。
$ sudo chmod a+x test1.sh
henryhu@henryhu-virtual-machine:~/bash-study$ ./test1.sh
2019年 09月 19日 星期四 20:28:49 CST
henryhu tty7 2019-09-19 10:49 (:0)
3. 显示消息
大多数shell命令都会产生自己的输出,这些输出会显示在脚本所运行的控制台显示器上。通过echo命令用户可以添加自己的消息来告诉脚本用户脚本正在做什么。
$ echo This is a test
This is a test
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语句的-n参数。
$ echo -n "The time and date are: "
The time and date are: 2019年 09月 19日 星期四 20:38:29 CST
4. 使用变量
通常如果需要在shell命令中使用其他数据来处理信息。这可以通过变量来实现。变量允许临时性地将信息存储在shell脚本中,以便和脚本中的其他命令一起使用。
4.1 环境变量
shell维护这一组环境变量,用来记录特定的系统信息。比如系统的名称、登录到系统上的用户名、用户的系统ID(也称为UID)、用户的默认主目录以及shell查找程序的搜索路径可以用set命令来显示一份完整的当前环境变量列表。
#!/bin/bash
# display user information from the system.
echo "User info for userid: $USER"
echo UID:: $UID
echo HOME: $HOME
UID、和$HOME环境变量用来显示已登录用户的有关消息。脚本输入如下:
henryhu@henryhu-virtual-machine:~/bash-study$ ./test2.sh
User info for userid: henryhu
UID:: 1000
HOME: /home/henryhu
4.2 用户变量
除了环境变量,shell脚本还允许在脚本中定义和使用自己的变量。定义变量允许临时存储数据并在整个脚本中使用,从而使shell脚本看起来更像一个真正的计算机程序。
用户变量可以是任何由字母、数字或下划线组成的文本字符串,长度不超过20个。用户变量区分大小写,所以变量var1和变量Var1是不同的。
使用等号将值赋给用户变量。在变量、等号和值之间不能出现空格。
var1=10
var2=-57
var3=testing
var4="still more testing"
shell脚本会自动决定变量值的数据类型。在脚本的整个生命周期里,shell脚本中定义的变量会一直保持者它们的值,但在shell脚本结束时会被删除。
与系统变量类似,用户变量可通过美元符引用。
#!/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"
运行脚本会输出:
Katie checked in 10 days ago
Jessica checked in 5 days ago
变量每次被引用时,都会输出当前赋给它的值。引用一个变量值时需要使用美元符($)。 没有美元符,shell会将变量解释成普通的文本字符串。
4.3 命令替换
shell脚本中最有用的特性之一就是可以从命令输出中提取信息,并将其赋给变量。
有两种方法可以将命令输出赋给变量:
- 反引号字符(`)
- $()格式
命令替换允许你将shell命令的输出赋给变量。要么用一对反引号把整个命令行命令围起来:
testing=`date`
要么使用$()格式:
testing=$(date)
shell会运行命令替换符号中的命令,并将其输出赋给变量testing。注意,赋值等号和命令替换字符之间没有空格。
#!/bin/bash
testing=$(date)
echo "The date and time are: " $testing
变量testing获得了date命令的输出,然后使用echo语句显示它的值。运行这个shell脚本输出如下:
The date and time are: 2019年 09月 20日 星期五 10:44:13 CST
5. 重定向输入和输出
bash shell提供了几个操作符,可以将命令的输出重定向到另一个位置(比如文件)。重定向可以用于输入,也可以输出,可以将文件重定向到命令输入。
5.1 输出重定向
最基本的重定向将命令的输出发送到一个文件中。bash shell用(>)来完成这项功能:
command > outputfile
之前显示器上出现的命令输出会被保存到指定的输出文件中。
$ cat test5
$ ls -l test5
-rw-rw-rw- 1 root root 0 9月 20 11:00 test5
$ cat test5
2019年 09月 20日 星期五 11:02:03 CST
重定向操作符创建了一个文件test5(通过默认的umask设置),并将date命令的输出重定向到该文件中。如果输出文件以及存在了,重定向操作符会用新的文件数据覆盖已有文件。
如果不需要覆盖输出,可以用双大于号(>>)来追加数据。
5.2 输入重定向
输入重定向和输出重定向正好相反。输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。
输入重定向符号是小于号(<):
还有另外一种输入重定向的方法,称为内联输入重定向。这种方法无需使用文件进行重定向,只需要在命令行中指定用于重定向的数据就可以了。
内联输入重定向符号是远小于号(<<)。除了这个符号,你必须指定一个文本标记来划分输入数据的开始和结尾。任何字符串都可作为文本标记,但在数据的开始和结尾文本标记必须一致。
command << marker
data
marker
在命令行上使用内联输入重定向时,shell会用PS2环境变量中定义的次提示符来提示输入数据。
$ wc << EOF
> test string 1
> test string 2
> test string 3
> EOF
3 9 42
6. 管道
有时需要将一个命令的输出作为另一个命令的输入。这可以用重定向来实现。最直接的一种方式是通过管道连接。
command1 | command2
7. 执行数学运算
在shell脚本中有两种途径来进行数学运算。
7.1 expr命令
最开始,Bourne shell提供了一个特别的命令用来处理数学表达式。expr命令允许在命令行上处理数学表达式,但是特别笨拙。
$ expr 1 + 5
6
expr命令能够识别少数的数学和字符串操作符,尽管标准操作符在expr命令中工作得很好,但在脚本或命令行上使用它们仍有问题出现。
$ expr 5 * 2
expr: syntax error
要解决这个问题,对于那些容易被shell错误解释的字符,在它们传入expr命令之前,需要使用shell的转义字符(反斜线)将其标出来。
$ expr 5 \* 2
10
在shell脚本中使用expr命令也同样复杂
#!/bin/bash
# An example of using the expr command
var1=10
var2=20
var3=$(expr $var2 / $var1)
echo The result is $var3
The result is 2
7.2 使用方括号
bash shell为了保持跟Bourne shell的兼容而包含了expr命令,但它同样也提供了一种更简单的方法来执行数学表达式。在bash中,在将一个数学运算结果赋给某个变量时,可以用美元符和方括号($[operation])将数学表达式围起来。
#!/bin/bash
var1=$[1+5]
echo $var1
var2=$[$var1*2]
echo $var2
6
12
8. 退出脚本
shell中运行的每个命令都使用退出状态码告诉shell它已经运行完毕。退出状态码是一个0-255的整数值,在命令结束运行时由命令传给shell。可以捕获这个值并在脚本中使用。
8.1 查看退出状态码
Linux提供了一个专门的变量?变量。它的值会变成由shell所执行的最后一条命令的退出状态码。
$ date
2019年 09月 20日 星期五 14:12:29 CST
$ echo $?
0
8.2 exit命令
默认情况下,shell脚本会以脚本中最后一个命令的退出状态码退出。你可以改变这种默认行为,返回自己的退出状态码。exit命令允许你在脚本结束时指定一个退出状态码。
#!/bin/bash
# testing the exit status
var1=10
var2=30
var3=$[$var1 + $var2]
echo The answer is $var3
exit 5
$ echo $?
5