shell
脚本的关键在于输入多个命令并处理每个命令的结果,甚至需要将一个命令的结果传给另一个命令。shell
允许我们将多个命令串起来,一次执行完成。格式:多个命令放在一行中,彼此使用分号;
隔开。
如输出时间,和登录用户:date +%Y%m%d;who
qinwanlideMacBook-Pro:~ qinwanli$ date +%Y%m%d;who
20203210/27/20
qinwanli console Oct 27 10:08
qinwanli ttys000 Oct 27 10:31
qinwanlideMacBook-Pro:~ qinwanli$
上述date +%Y%m%d;who
组成了一个简单的脚本,只不过这个脚本只用到了两个bash shell
的命令。采用分号,分隔多命令的形式,会受到命令行最大字符数255
的限制,并且每次都需要重新输入。因此,我们会将复杂的脚本,放到一个文本文件中,每次只需重新运行这个文本文件即可。
1.shell
脚本文件创建与运行
关于shell
脚本文件的创建,我们需要知道如何运用编辑器创建一个文件,具体可查看shell文件编辑器。也需知道shell
文件内容的格式要求。
在创建shell
脚本文件时,必须在文件的第一行指定要使用的shell
。其格式为:
#! /bin/bash
通常在shell
脚本文件中,#
用来注释行,但是shell
脚本文件的第一行是例外,井号后的!
会指定要运行该脚本的shell
是哪一个(使用bash shell
编写脚本,指定其他脚本执行也是可行的,如#! /bin/zsh
)。
在shell
脚本文件中,可以将多个命令放在同一行中彼此用;
隔开,也可使用单独的行中书写。shell
会根据命令在文件中出现的顺序执行。
运行shell
脚本文件,需保证shell
能找到我们的shell
脚本文件,有两种方式:
- 将
shell
脚本文件所处的目录添加到PATH
环境变量中; - 在提示符中用绝对或相对文件路径来引用
shell
脚本文件;
接下来,我们将date +%Y%m%d;who
这个简单的脚本写入shell
脚本文件并执行,这个期间会经历一个重要的步骤,修改文件的权限:
#1.建个文件
vim first_script
#first_script文件内容:
#! /bin/bash:
#第一个用于测试的`shell`脚本
#date +%Y%M%D
#who
#2.修改文件权限
chmod u+x first_script
#3.使用相对路劲执行脚本
./first_script
#4.输出
20201410/27/20
qinwanli console Oct 27 10:08
qinwanli ttys000 Oct 27 10:31
脚本运行的过程中,可以通过echo
命令输出一些文本信息,来告诉用户,此刻脚本正在做什么。
echo
命令输出文本时,默认不需要使用引号,就能显示文本。
qinwanlideMacBook-Pro:~ qinwanli$ echo good job
good job
特殊情况,字符串中出现引号,此时需要使用单引号或双引号,使用其中一个引用
echo
要输出的信息,使用另一个引用输出文本中带引号的文本。总之不能使用一样的引号。
qinwanlideMacBook-Pro:~ qinwanli$ echo 'Rich says "scripting is easy".'
Rich says "scripting is easy".
qinwanlideMacBook-Pro:~ qinwanli$ echo "Rich says 'scripting is easy'."
Rich says 'scripting is easy'.
qinwanlideMacBook-Pro:~ qinwanli$
为我们的脚本文件加点文本消息:
#! /bin/bash:
#第一个用于测试的`shell`脚本
echo 输出系统的时间
date
echo 谁在登录
who
执行后输出:
输出系统的时间
2020年10月27日 星期二 11时34分06秒 CST
谁在登录
qinwanli console Oct 27 10:08
qinwanli ttys000 Oct 27 11:20
文本消息与命令执行的结果处于同一行:echo -n msg
:
修改脚本:echo -n 系统时间:;date
执行输出:系统时间:2020年10月27日 星期二 11时37分20秒 CST
2.使用变量
echo
命令中的环境变量会在脚本运行时替换成当前值,变量替换。shell
脚本文件中使用环境变量的两种方式:
- 变量名可被清楚识别时:在环境变量前加
$
。
#! /bin/bash
echo 当前用户:$USER #当前用户:qinwanli
echo "用户id:$UID 输出" #用户id:501 输出
echo "用户的主目录:$HOME"#用户的主目录:/Users/qinwanli
#注意
#单引号,所引用变量,不会被解读
echo '用户id:$UID 输出' #输出:用户id:$UID 输出
- 变量名不能被清楚识别时:
${variable}
。
#! /bin/bash
echo 用户${USER}已经开机
echo "用户${USER}已经开机"
注意:
echo
输出文本中引用变量,使用单引号,变量不会被解读,只能使用双引号"
。
除了环境变量,shell
允许用户在脚本中定义和使用变量。定义变量允许临时存储数据并在整个脚本中使用。
var1=hello;var2=world
var3=56
var4="你好,世界"
shell
脚本会自动决定变量值的数据类型。在脚本的整个生命周期里,shell
脚本中定义的变量会一直保持着它们的值,直到shell
脚本结束时被删除掉。
与系统变量类似,用户变量可通过美元符$
引用,或${variable}
引用。
注意:引用一个变量的值时需要使用
$
,而引用别的变量来对其进行赋值时则不要使用$
。
var1=hello;var2=world
echo $var1 $var2 #hello world
var1=hello;var2=world
var3=$var2
var4="$var3 !"#变量定义,变量值中有空格时,`bash shell`就会把值当成一个单独的命令,必须使用引号。
echo "$var1 $var4"#hello world !
3.命令替换
命令替换:从命令输出中提取信息,并将其赋给变量。
有两种方式可以将命令输出赋值给变量:
- 反引号字符
#! /bin/bash
var=`date +%Y%m%d`
echo $var #输出:20201027
-
$()
格式
#! /bin/bash
var=$(date +%Y%m%d)
echo $var #输出:20201027
注意:命令替换会创建一个子
shell
来运行对应的命令。
4.重定向输入和输出
bash shell
提供了几个操作符,可以将命令的输出重定向到另一个位置(比如文件)。重定向可以用于输入,也可以 用于输出,可以将文件重定向到命令输入。
输出重定向
最基本的重定向将命令的输出发送到一个文件中,bash shell
用大于号(>
)来完成这项功能,基本的命令格式:command > outputfile
。
#时间输出到桌面的file1文件中
date > ~/desktop/file1
#查看
vim ~/desktop/file1
#显示
2020年10月27日 星期二 16时02分52秒 CST
#登录用户输出到file1中
who > ~/desktop/file1
#查看
vim ~/desktop/file1
#显示
qinwanli console Oct 27 10:08
qinwanli ttys000 Oct 27 15:44
综上命令执行,我们发现 >
重定向输出到同一个文件中时,文件中的内容会被覆盖。
如果并不想覆盖文件原有内容,而是想要将命令的输出追加到已有文件中则使用双大于号>>
命令。
#继续追加数据到file1
date >> ~/desktop/file1
#查看
vim ~/desktop/file1
#显示
qinwanli console Oct 27 10:08
qinwanli ttys000 Oct 27 15:44
2020年10月27日 星期二 16时14分11秒 CST
输入重定向
输入重定向将文件的内容重定向到命令。
操作符为小于号<
,格式为:command < inputfile
。
#文件内容输入到命令
wc < ~/desktop/file1
#输出
3 14 112 file1
wc
命令会输出,输入的文本文件有多少行,多少单词,多少字节。
还有另外一种输入重定向的方法,称为内联输入重定向(inline input redirection
)。这种方法无需使用文件进行重定向,只需要在命令行中指定用于重定向输入的数据就可以了。
内联输入重定向的操作符:双小于号<<
,除了这个符号,我们还必须指定一个文本标记来划分输入数据的开始和结尾。任何字符串都可作为文本标记,但在数据的开始和结尾文本标记必须一致。
格式为:
command << marker#可以任何字符串,首尾必须一致
data
marker #可以任何字符串,首尾必须一致
注意:在命令行上使用内联输入重定向时,shell
会用PS2
环境变量中定义的次提示符。
具体使用:
#输入
wc << tag
#回车,会出现次提示符,输入内容,
> 大漠孤烟直
> 长河落日圆
#次提示符会持续提示,以获取更多的输入数据,直到我们输入了作为文本标记的那个字符串
> tag #意味输入数据结束,回车会执行`wc`
#输出
2 3 32
5.管道
有时需要将一个命令的输出作为另一个命令的输入。也可以用重定向来实现,只不过有些笨拙,比如:
#文件信息重定向输出到file
ls -l > file
#对文件进行排序输出
sort < file
#输出排序后的结果
一个命令的输出作为另一个命令的输入时,不需要将信息重定向到文件中,便可以将其直接重定向到另一个命令的过程,即为:管道连接
。管道操作符:|
,格式:command1 | command2
。
注意:管道串起的两个命令并不是依次执行的。
Linux
系统实际上会同时运行这两个命令,在系统内部将它们连接起来。在第一个命令产生输出的同时,输出会被立即送给第二个命令。数据传输不会用到任何中间文件或缓冲区。
具体使用:
#列举目录下的内容,并排序
qinwanlideMacBook-Pro:test qinwanli$ ls | sort
#输出
AppDelegate.h
AppDelegate.m
Assets.xcassets
Base.lproj
Info.plist
SceneDelegate.h
SceneDelegate.m
ViewController.h
ViewController.m
main.m
输出数据量比较大时,为了方便查看也可以管道执行more
或less
:
ls | sort | more
管道与重定向输出配合使用
ls | sort > file
6.数学运算
shell
脚本中有两种方式来处理数学运算:
-
expr
命令; - 使用方括号
[ ]
expr
执行数学运算
expr
执行数学运算,支持的操作符:
终端使用:
expr 1 + 5
#输出:6
expr 3 * 5
#输出:expr: syntax error
#解决办法,`*`加转义`\`
expr 3 \* 5
#输出:15
shell
脚本使用expr
命令比较复杂,需要使用命令替换来获取expr
命令的输出:
#! /bin/bash
#sample
var1=3;var2=4
echo "执行运算:$var1 * $var2"
var3=$(expr $var1 \* $var2)
echo 输出:$var3
执行脚本输出:
执行运算:3 * 4
输出:12
使用方括号
bash shell
为了保持跟Bourne shell(sh)
的兼容而包含了expr
命令,但它同样也提供了一种更简单的方法来执行数学表达式:$[operation]
。
终端使用:
echo $[1+5]
#输出:6
echo $[3*5]
#输出:15
脚本执行:
#! /bin/bash
#sample
var1=3;var2=4
echo "执行运算:$var1 * ($var2 - 1)"
var3=$[$var1 * ($var2 - 1)]
echo 输出:$var3
执行脚本输出:
执行运算:3 * (4 - 1)
输出:9
在bash shell
中使用方括号进行数学运算的最大的限制时浮点数的计算:
echo $[3/2]
#输出
1
在bash
中数学运算支持整数运算。
值得一提的是
zsh
,提供了完整的浮点数算术操作。
bash
中浮点数算术运算
那么如何在bash
中进行浮点数的算术操作呢,答案是使用bash
中内置的计算器,使用命令:bc
。
bash
计算器(bc
),能够识别:
- 数字(整数和浮点数)
- 变量(简单变量和数组)
- 注释(以#或C语言中的/* */开始的行)
- 表达式
- 编程语句(例如if-then语句)
- 函数
在终端使用bc
进行运算:
#输入`bc`
qinwanlideMacBook-Pro:test qinwanli$ bc
#输出
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
#出现运算输出提示符
#输入
3.14 * 2
#回车,输出结果
6.28
#要退出`bc`时,必须输入`quit`
quit
#返回
qinwanlideMacBook-Pro:test qinwanli$
要退出bc
时,必须输入quit
。
浮点运算是由内建变量scale
控制的。必须将这个值设置为我们希望在计算结果中保留的小数位数,否则无法得到期望的结果:
qinwanlideMacBook-Pro:test qinwanli$ bc
#默认的scale=0
3.14 /5
0
#设置scale=2
scale=2
3.14 /5
.62
#设置scale=4
scale=4
3.14 /5
.6280
quit
-q
命令,可以在启动bash
计算器时不显示其冗长的介绍信息。
bc -q
#开始输入
3*4
12
在bc
中使用变量:
qinwanlideMacBook-Pro:test qinwanli$ bc -q
#定义变量
var1=3
var2=7
var3=5
#定义变量表达式
var4=var1*(var2-var3)
#利用print输出结果
print var4
6
quit
qinwanlideMacBook-Pro:test qinwanli$
在脚本中使用bc
:
在脚本中使用bc
,主要依赖于命令替换,将bc
计算的结果赋值给脚本中定义的变量,脚本使用bc
的基本格式:variable=$(echo "options; expression" | bc)
,解读:echo
输出的字符串,通过管道输入到bc
,进行执行后,输出,使用命令替换,将结果赋值给变量variable
。
脚本使用bc
实例如下:
#! /bin/bash
#脚本中使用bc
echo 脚本中使用bc计算示例如下:
echo 1.定义脚本变量
var1=12.25
var2=4.55
echo 2.计算var1*var2使用'bc'进行计算,并将'bc'输出的结果,直接输出
echo $(echo "scale=4;$var1*$var2" |bc)
echo 3.计算var1/var2,将'bc'输出的结果,赋值给一个变量var3
var3=$(echo "scale=2;$var1/$var2"|bc)
echo 输出var3=$var3
echo 4.计算var3*var1,将'bc'输出的结果,赋值给一个变量var4,并输出
var4=$(echo "scale=4;$var3*$var1"|bc)
echo 最终输出:var4=$var4
执行脚本后输出
qinwanlideMacBook-Pro:desktop qinwanli$ ./first_script
脚本中使用bc计算示例如下:
1.定义脚本变量
2.计算var1*var2使用bc进行计算,并将bc输出的结果,直接输出
55.7375
3.计算var1/var2,将bc输出的结果,赋值给一个变量var3
输出var3=2.69
4.计算var3*var1,将bc输出的结果,赋值给一个变量var4,并输出
最终输出:var4=32.9525
qinwanlideMacBook-Pro:desktop qinwanli$
当脚本中使用bc
,需要进行大量运算,在一个表达式中列出许多表达式比较麻烦,如:
var4=$(echo "scale=4;var5=$var3*$var1;var5*10"|bc)
bc
可以识别内联重定向,因此可以使用这种可以使用内联重定向,将计算的表达式,输入到bc
,格式如下:
variable=$(bc << EOF
options
statements
expressions
EOF )
使用示例
#! /bin/bash
#脚本中使用bc
echo 脚本中使用bc计算示例如下:
echo 1.定义脚本变量
var1=12.25
var2=4.55
echo 2.采用内联重定向数据
var4=$(bc << input
scale=3
var3=$var1*$var2
var5=$var1+$var2
var3+var5
input
)
echo 最终输出:var4=$var4
执行输出
qinwanlideMacBook-Pro:desktop qinwanli$ ./first_script
脚本中使用bc计算示例如下:
1.定义脚本变量
2.采用内联重定向数据
最终输出:var4=72.537
qinwanlideMacBook-Pro:desktop qinwanli$
7.退出脚本
退出脚本:shell
中运行的每个命令都使用退出状态码(exit status)告诉shell
它已经运行完毕。退出状态码是一个0~255的整数值。
默认情况下,命令成功退出的状态码是0
:
qinwanlideMacBook-Pro:~ qinwanli$ date
2020年10月27日 星期二 20时57分31秒 CST
qinwanlideMacBook-Pro:~ qinwanli$ echo $?
0
使用echo $?
可以查看某个命令退出的状态码。默认情况下,shell
脚本会以脚本中的最后一个命令的退出状态码退出。我们也可以主动为我们的脚本添加退出的状态码,保证它在0~255
的范围内,也可以指定变量
#! /bin/bash
var=4
exit $var
#指定数字
exit 0
参考资料:
Linux命令行与shell脚本编程大全