基础篇
一、输出信息
大部分 Shell 命令都会生成自己的输出信息,在脚本运行时打印到终端屏幕上。但是很多时候,仍需要在输出的信息中添加上自己的内容,以提示用户脚本运行时究竟发生着什么,达到更好的交互效果。
echo
命令可以用来打印字符串内容。1
2
3$ echo This is a test
This is a test
$
PS:默认是不需要将 echo
命令后面的字符串包含在一对引号中的
echo
可以使用引号作为文本字符串的分隔符。如:1
2
3
4$ echo 'Hello
> World'
Hello
World
当输出的文本内容中本来就有引号出现时,如(Let's see if this'll work
),可以结合单、双引号的使用,或者使用转义符(\
)前缀1
2
3
4
5
6
7
8
9# 引号作为字符串分隔符而不是文本内容
$ echo Let's see if this'll work
Lets see if thisll work
# 使用 \ 前缀进行转义
$ echo Let\'s see if this\'ll work
Let's see if this'll work
# 使用双引号作为分隔符,单引号作为中间的文本正常输出
$ echo "Let's see if this'll work"
Let's see if this'll work
脚本的执行权限
默认创建的脚本文件没有执行权限,不能直接在命令行中运行。
如创建包含以下内容的脚本文件 info.sh
:1
2
3
4
5
6#!/bin/bash
echo The time and date are:
date
echo "Let's see who's logged into the system:"
who
直接执行上述脚本时会提示 permission denied
错误:1
2$ ./info.sh
zsh: permission denied: ./info.sh
需要使用 chomd +x filename
命令为该脚本文件添加执行权限后再运行,效果如下:1
2
3
4
5
6
7
8$ chmod +x info.sh
$ ./info.sh
The time and date are:
Wed Oct 31 11:48:07 CST 2018
Let's see who's logged into the system:
starky console Oct 28 12:42
starky ttys000 Oct 29 16:52
starky ttys001 Oct 31 11:23
二、变量
1. 系统环境变量
在 Shell 脚本中可以直接访问系统中的环境变量,以获取相关的系统信息(如计算机名称,当前登录用户的账户名、 UID 和主目录等)。
当前定义的所有环境变量可以通过 set
命令获取。1
2
3
4
5
6
7
8
9
10$ set
...
HISTCMD=2217
HISTFILE=/Users/starky/.zsh_history
HISTSIZE=50000
HOME=/Users/starky
HOST=skitars-MacBook-Pro.local
IFS=$' \t\n\C-@'
ITERM_PROFILE=starky
...
当需要在 Shell 脚本中使用具体某个环境变量的值时,可以用环境变量名称加上 $
前缀表示(如 $HOST
)。
编辑如下 sys_info.sh
文件:1
2
3
4
5#!/bin/bash
echo User info for userid: $USER
echo UID: $UID
echo HOME: $HOME
运行效果如下:1
2
3
4
5$ chmod +x sys_info.sh
$ ./sys_info.sh
User info for userid: starky
UID: 501
HOME: /Users/starky
PS:由于在 Shell 脚本中,$
作为变量的前缀符,所以当需要在文本输出中显示 $
时,应使用转义。1
2
3
4
5
6# $15 被当成了代入到字符串中的“变量”
$ echo "The cost of the item is $15"
The cost of the item is
# 使用 \ 转义后正常打印 $ 字符
$ echo "The cost of the item is \$15"
The cost of the item is $15
2. 用户自定义变量
Shell 脚本允许用户自行定义和使用变量,这样就可以将脚本中用到的数据临时存储在指定的变量中,使用时再通过 $变量名
的形式获取。
- 变量赋值:
var=value
(注意=
号两边不能有空格,即var = value
是错误的) - 变量使用:
$var
PS:Shell 脚本中的变量名区分大小写
Shell 脚本会自动判断变量值的数据类型
变量的有效性贯穿脚本的整个生命周期,即脚本执行完毕后变量会自行删除
编辑如下 variables.sh
文件:1
2
3
4
5
6
7
8
9#!/bin/bash
days=10
guest="Katie"
echo "$guest checked in $days days ago"
days=5
guest="Jessica"
echo "$guest checked in $days days ago"
运行效果如下:1
2
3
4
5$ chmod +x variables.sh
$ ./variables.sh
Katie checked in 10 days ago
Jessica checked in 5 days ago
$ echo $days
可以看到,脚本退出后,脚本中定义的 $days
变量又恢复为未定义的状态。
三、命令替换
Shell 脚本最有用处的特性之一,就是它可以提取某个命令的输出信息,并将其赋值给一个变量。
可以通过以下两种方式将命令输出赋值给变量:
- 反单引号(`)
$()
格式
如:1
2
3
4
5
6
7
8
9
10
11# 使用 date 命令获取当前的日期和时间
$ date
Thu Nov 1 01:03:02 CST 2018
# 将 date 命令的输出(即当前日期和时间)赋值给 var1 变量
$ var1=`date`
$ echo Today is: $var1
Today is: Thu Nov 1 01:03:15 CST 2018
# 将 date 命令的输出赋值给 var2 变量(使用 $() 格式)
$ var2=$(date)
$ echo Today is: $var2
Today is: Thu Nov 1 01:03:36 CST 2018
示例程序:
使用命令替换完成一个脚本(log.sh
),该脚本可以创建以当前时间水印为后缀的文本文件,内容为 /usr/bin
目录下的所有文件列表。1
2
3
4
5#!/bin/bash
today=$(date +%y%m%d%H%M%S)
ls -al /usr/bin > log.$today
echo The file log.$today has been created, you can check it later.
其中 date +%y%m%d%H%M%S
命令可以输出纯数字格式的日期和时间
运行效果如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16$ chmod +x log.sh
$ ./log.sh
The file log.181101012159 has been created, you can check it later.
$ ls log*
log.181101012159 log.sh
$ head log.181101012159
total 103992
drwxr-xr-x 971 root wheel 31072 Oct 13 17:44 .
drwxr-xr-x@ 9 root wheel 288 Sep 21 12:01 ..
-rwxr-xr-x 4 root wheel 925 Aug 18 08:45 2to3-
lrwxr-xr-x 1 root wheel 74 Oct 13 17:44 2to3-2.7 -> ../../System/Library/Frameworks/Python.framework/Versions/2.7/bin/2to3-2.7
-rwxr-xr-x 1 root wheel 55072 Sep 21 12:16 AssetCacheLocatorUtil
-rwxr-xr-x 1 root wheel 53472 Sep 21 12:16 AssetCacheManagerUtil
-rwxr-xr-x 1 root wheel 48256 Sep 21 12:17 AssetCacheTetheratorUtil
-rwxr-xr-x 1 root wheel 18320 Sep 21 12:17 BuildStrings
-rwxr-xr-x 1 root wheel 18288 Sep 21 12:17 CpMac
四、重定向输入和输出
1. 输出重定向
最基本的重定向,就是通过大于号(>),将某个命令的输出内容保存至一个文件中。
格式:command > outputfile
1 | $ date > current_date.txt |
PS:如使用重定向时,指定的文件已存在,则该文件的原始内容会被新内容覆盖。
如果只是想在文件末尾追加内容,则可以使用双大于号(>>)1
2
3
4$ date >> current_date.txt
$ cat current_date.txt
Thu Nov 1 01:29:45 CST 2018
Thu Nov 1 01:34:03 CST 2018
2. 输入重定向
输入重定向和输出重定向相反。即从文件中读取内容,并将该内容传递给某个命令。
格式:command < inputfile
1 | $ ls -l /Users/starky |
3. 管道
有些时候,需要将某个命令的输出内容作为另一个命令的输入。如:1
2
3
4
5
6$ ls -al > tmp_file
$ grep vim < tmp_file
drwxr-xr-x 3 starky staff 96 Oct 20 15:45 .vim
-rw------- 1 starky staff 23799 Nov 1 01:40 .viminfo
-rw-r--r-- 1 starky staff 3935 Oct 21 01:31 .vimrc
-rw-r--r-- 1 starky staff 24849808 Oct 25 21:02 vim.tar.gz
上面的命令先将当前目录下的文件列表(ls -al
)保存在 tmp_file
中,再使用 grep
命令读取 tmp_file 的内容,筛选文件名中包含 vim 的文件。
其实可以通过管道(|
)的使用,将前面命令的输出,定向给后面的命令作为输入。
格式:command1 | command2
1 | ls -al | grep vim |
五、数学运算
操作数学运算对于任何编程语言来说,都是一个很重要的特性。但是 Shell 脚本并不能直接完成算术运算的操作,只能通过以下两种方式来实现。
1. expr 命令
Shell 提供了一个特殊的命令(expr
)用来处理数学算式,如:1
2$ expr 1 + 2
3
PS:注意算式中 +
号两边的空格
expr 命令支持的算术操作符如下:
操作符 | 含义 |
---|---|
ARG1 双管道符 ARG2 | 如果两个参数值都不为 null 或 0,返回 ARG1,否则返回 ARG2 |
ARG1 && ARG2 | 如果两个参数值都不为 null 或 0,返回 ARG1,否则返回 0 |
ARG1 < ARG2 | 如果 ARG1 小于 ARG2,返回 1,否则返回 0 |
ARG1 > ARG2 | 如果 ARG1 大于 ARG2,返回 1,否则返回 0 |
ARG1 = ARG2 | 如果 ARG1 等于 ARG2,返回 1,否则返回 0 |
ARG1 >= ARG2 | 如果 ARG1 大于或等于 ARG2,返回 1,否则返回 0 |
ARG1 <= ARG2 | 如果 ARG1 小于或等于 ARG2,返回 1,否则返回 0 |
ARG1 != ARG2 | 如果 ARG1 不等于 ARG2,返回 1,否则返回 0 |
ARG1 + ARG2 | 返回 ARG1 与 ARG2 的数字加和 |
ARG1 - ARG2 | 求 ARG1 减去 ARG2 的数字差 |
ARG1 * ARG2 | 返回 ARG1 与 ARG2 的数字乘积 |
ARG1 / ARG2 | 求 ARG1 除以 ARG2 的数字商(结果为整数) |
ARG1 % ARG2 | 对 ARG1 和 ARG2 进行求余操作 |
数学运算示例(divide.sh
):
1 | #!/bin/bash |
运行效果:1
2
3$ chmod +x divide.sh
$ ./divide.sh
20 divided by 10 equals 2
2. 使用中括号
Bash Shell 中的 expr
命令主要是为了保持和 Bourne Shell 的兼容性,它其实还提供了一种更简单的方式用来处理数学运算。即使用这样的形式:$[ operation ]
如下面的脚本(compute.sh
)1
2
3
4
5
6
7
8#!/bin/bash
var1=100
var2=50
var3=45
result=$[$var1 * ($var2 - $var3)]
echo The final result is $result
运行效果:1
2
3$ chmod +x compute.sh
$ ./compute.sh
The final result is 500
但是在进行除法运算时,上面的方式只支持整数。如两个数相除结果为小数,则该结果会舍去小数部分只保留整数。1
2
3
4
5$ var1=10
$ var2=3
$ result=$[$var1 / $var2]
$ echo The result is $result
The result is 3
3. 浮点数运算
有很多种方案可以克服 bash 的整数限制,最常用的一种就是使用系统内置的 bash calculator,即 bc
程序。
bash calculator 其实是一种支持浮点数运算的编程语言,可以识别以下几种类型的数据:
- 数字(整数和浮点数)
- 变量(简单变量和数组)
- 注释(单行注释
#
和多行注释/* */
) - 表达式
- 编程语句(如
if-then
语句) - 函数
1 | $ bc |
计算结果精确到的小数位数是通过一个内建的 scale
变量定义的,默认的 scale 数值为 0(即默认舍去商的小数位数):1
2
3
4
5
6
7$ bc -q
3.44 / 5
0
scale=4
3.44 / 5
.6880
quit
bc
程序也支持自定义变量:1
2
3
4
5
6
7
8$ bc -q
var1=10
var1 * 4
40
var2=var1 / 5
print var2
2
quit
4. 在脚本中使用 bc
可以通过管道将数学表达式传递给 bc
程序,再将计算得出的结果通过赋值语句赋值给某个变量:variable=$(echo "options; expression" | bc)
其中 variable=$(...)
用于提取命令的输出并赋值给一个变量(参考第三章命令替换)
如下面的脚本(bc1.sh
):1
2
3
4
5
6
7#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc)
echo The answer for this is $var3
运行效果:1
2
3$ chmod +x bc1.sh
$ ./bc1.sh
The answer for this is 2.2222
更复杂的形式
在脚本中使用 bc
还可以通过如下的形式:1
2
3
4
5
6variable=$(bc << EOF
options
statements
expressions
EOF
)
示例程序如下(bc2.sh
):
1 | #!/bin/bash |
运行结果:1
2
3$ chmod +x bc2.sh
$ ./bc2.sh
The final answer for this mess is 2813.9882