Shell系列之构建脚本

移动端团队 @ 奇舞团(360集团大前端团队)

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的限制,并且每次都需要重新输入。因此,我们会将复杂的脚本,放到一个文本文件中,每次只需重新运行这个文本文件即可。

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

使用变量

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 !
复制代码

命令替换

命令替换:从命令输出中提取信息,并将其赋给变量。 有两种方式可以将命令输出赋值给变量:

  • 反引号字符
#! /bin/bash
var=`date +%Y%m%d`
echo $var #输出:20201027
复制代码
  • $()格式
#! /bin/bash
var=$(date +%Y%m%d)
echo $var #输出:20201027
复制代码

注意:命令替换会创建一个子shell来运行对应的命令。

重定向输入和输出

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
# step1: 回车,会出现次提示符,输入内容,
> 大漠孤烟直
> 长河落日圆
# tip: 次提示符会持续提示,以获取更多的输入数据,直到我们输入了作为文本标记的那个字符串
> tag # 意味输入数据结束,回车会执行`wc`
#输出
 2       3      32
复制代码

管道

有时需要将一个命令的输出作为另一个命令的输入。也可以用重定向来实现,只不过有些笨拙,比如:

#文件信息重定向输出到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
复制代码

输出数据量比较大时,为了方便查看也可以管道执行moreless:

ls | sort | more
复制代码

管道与重定向输出配合使用

ls | sort > file
复制代码

数学运算

shell脚本中有两种方式来处理数学运算:

  • expr命令;
  • 使用方括号[ ]

expr执行数学运算

expr执行数学运算,支持的操作符: expr支持的操作符.png

终端使用:

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中内置的计算器,使用命令:bcbash计算器(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$ 
复制代码

退出脚本

退出脚本: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脚本编程大全

文章分类
后端
文章标签