Shell构建脚本

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

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

ls | sort | more

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

ls | sort > file

6.数学运算

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中内置的计算器,使用命令: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脚本编程大全

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260