本博客内容来自《Linux命令行与shell脚本编程大全》第十一章
目录
1、使用多个命令
2、创建shell脚本文件
3、显示消息
4、使用变量
环境变量
用户变量
命令替换(重要)
5、重定向输入和输出
输出重定向
输入重定向
6、管道
7、执行数学运算
expr命令
使用方括号
浮点解决方案
bc的基本用法
在脚本中使用bc
8、退出脚本
查看退出状态码
exit命令
最简单的脚本:在命令行界面直接输入多个bash shell命令,用分号隔开
[root@izwz9194nuv8g0cwqfqsh3z ~]# date;who
Sun Mar 3 21:40:11 CST 2019
root pts/0 2019-03-03 20:17 (183.157.160.36)
如上所示,date显示当前日期和时间,who显示是谁登陆到了系统上。
必须在文件第一行指定要使用的shell,其格式为
#!/bin/bash
#后面的一般为注释,shell不处理,但是第一行是例外。惊叹后后面会告诉shell用哪个shell来运行脚本。
在shell脚本中不需要分号隔开命令,回车行即可
#!/bin/bash
# this is an example
date
who
将以上内容保存为名为test的脚本文件,接下来运行脚本,两种途径:
1.将shell脚本文件所处的目录添加到PATH环境变量中;
2.在提示符中用绝对或相对文件路径来引用shell脚本文件。
我们一般采用第二种方式,将脚本文件的确切位置告诉shell,为了引用当前目录下的文件,可以在shell中使用单点操作符:
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test1
-bash: ./test1: Permission denied
我们没有执行文件的权限,查看文件权限:
[root@izwz9194nuv8g0cwqfqsh3z ~]# ll test1
-rw-r--r-- 1 root root 21 Mar 4 21:40 test1
我们发现文件没有被赋予执行权限,使用chmod命令赋予文件属主执行权限:
[root@izwz9194nuv8g0cwqfqsh3z ~]# chmod u+x test1
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test1
Mon Mar 4 21:45:45 CST 2019
root pts/0 2019-03-04 21:29 (183.157.160.23)
搞定!
显示消息一般用echo命令,一般不需要引号就可以显示出文本字符串,如:
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo this is a test
this is a test
但是如果文本字符串中有引号就需要注意,要加引号,如果有单引号就套双引号,有双引号就套单引号:
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo let's see if this'll work
lets see if thisll work
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo "let's see if this'll work"
let's see if this'll work
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo he said "good job"
he said good job
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo 'he said "good job"'
he said "good job"
如果想把文本字符串和命令输出显示在同一行,可以加 -n 参数:
编辑一个名为test1的文件,输入以下内容并保存退出:
#!/bin/bash
echo the time is :
date
echo the user is :
who
执行脚本:
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test1
the time is :
Wed Mar 6 10:28:57 CST 2019
the user is :
root pts/0 2019-03-06 10:17 (183.157.160.23)
可以看到echo输出与命令输出不在同一行,在脚本中的echo命令后加参数-n:
#!/bin/bash
echo -n the time is :
date
echo -n the user is :
who
执行脚本:
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test1
the time is :Wed Mar 6 10:31:20 CST 2019
the user is :root pts/0 2019-03-06 10:17 (183.157.160.23)
使用set命令可以显示当前环境变量完整的列表
[root@izwz9194nuv8g0cwqfqsh3z ~]# set
BASH=/bin/bash
......
-gnu")
BASH_VERSION='4.2.46(2)-release'
COLUMNS=187
DIRSTACK=()
EUID=0
GROUPS=()
HISTCONTROL=ignoredups
HISTFILE=/root/.bash_history
HISTFILESIZE=1000
HISTSIZE=1000
HOME=/root
HOSTNAME=izwz9194nuv8g0cwqfqsh3z
......
在脚本中,可以在环境变量名称前加上美元符$来使用上述的环境变量,也可以用${变量名}来显示,这样做的好处时变量名不会有歧义。但当我们想显示美元符号时,记得在$符号前加上反斜线\。
shell脚本允许在脚本中定义和使用自己的变量,这些变量允许临时存储数据并在整个脚本中使用;在shell脚本结束时,定义的变量会被删除掉。
使用等号将值赋给用户变量,在变量、等号和值之间不能出现空格。
用户变量也可以使用$符号引用。
shell脚本最有用的特性之一就是可以从命令输出中提取信息,并将其赋予变量。把输出赋给变量之后就可以在脚本中随意使用了。
两种方法可以将命令输出赋给变量:
反引号字符(`):result=`date`
$()格式:result=$(date)
例如,有以下名为test2的脚本内容:
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test2
#!/bin/bash
result=`date`
echo the time is: ${result}.
result=$(who)
echo "the user is:" ${result}.
执行该脚本可以得到:
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test2
the time is: Wed Mar 6 11:20:01 CST 2019.
the user is: root pts/0 2019-03-06 10:17 (183.157.160.23) root pts/1 2019-03-06 11:04 (183.157.160.23).
来一个比较体现价值的操作,输出以日期为后缀的日志文件:
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test3
#!/bin/bash
# copy the /usr/bin directory listing to a log file
today=$(date +%y%m%d)
ls /usr/bin -al > log.${today}
执行后输出日志文件:
-rw-r--r-- 1 root root 47943 Mar 6 11:26 log.190306
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat log.190306
total 292940
dr-xr-xr-x. 2 root root 20480 Mar 1 13:42 .
drwxr-xr-x. 13 root root 4096 Feb 24 10:19 ..
-rwxr-xr-x 1 root root 41544 Oct 31 03:16 [
-rwxr-xr-x 1 root root 107904 Jan 22 06:10 a2p
-rwxr-xr-x 1 root root 29200 Oct 30 17:55 addr2line
-rwxr-xr-x 1 root root 29 Oct 31 01:07 alias
lrwxrwxrwx 1 root root 6 Feb 24 10:20 apropos -> whatis
-rwxr-xr-x 1 root root 62736 Oct 30 17:55 ar
......
书本上说
命令替换本质上是运行该脚本的shell创建了一个子shell来运行对应的命令,因此该子shell中所执行的命令不能使用脚本中创建出来的变量。
针对这句话我做了个简单测试:
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test4
#!/bin/bash
var=Mike
result=`echo "the man's name is ${var}"`
echo ${result}
result=`echo "the user's name is ${USER}"`
echo ${result}[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test4
the man's name is Mike
the user's name is root
结果好像与书本所提到的相反,大家可以自己再测试一下。
如果想保存某个命令的输出而不是仅仅在界面显示,需要用到重定向功能。
使用大于号>将命令输出重定向到某个文件:
[root@izwz9194nuv8g0cwqfqsh3z ~]# date > test6
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test6
Wed Mar 6 15:27:49 CST 2019
此时会新建test6文件并写入内容,如果test6已经存在,则已有内容会被覆盖。
使用>>可以将命令输出重定向,追加到某个文件末尾
[root@izwz9194nuv8g0cwqfqsh3z ~]# who >> test6
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test6
Wed Mar 6 15:27:49 CST 2019
root pts/0 2019-03-06 15:27 (183.157.160.51)
输入重定向将文件的内容重定向到命令
输入重定向符号是小于号<,可以发现,与输出重定向相同的地方在于命令在左,文件在右。
command < inputfile
以下为与wc命令一起使用输入重定向的例子:
[root@izwz9194nuv8g0cwqfqsh3z ~]# wc test6
2 11 85 test6
[root@izwz9194nuv8g0cwqfqsh3z ~]# wc < test6
2 11 85
wc命令会输出文件中文本的行数、词数与字节数。
还有种输入重定向方法叫内联输入重定向,无需使用文件进行重定向,只需要在命令行中指定用于输入重定向的数据就行。
内联输入重定向使用<<符号,并且要指定一个文本标记符来划分输入数据的开始与结尾——任何字符串都可以作为文本标记,但必须一致。
command << marker
data
marker
[root@izwz9194nuv8g0cwqfqsh3z ~]# wc << END
> tes1 string 1
> test string 2
> test string 3
> END
3 9 42
如果想将一个命令的输出作为另一个命令的输入,使用重定向很麻烦,可以使用管道:
command1 | command2......
Linux系统实际上会同时执行这两个命令,在系统内部将其连接起来,在第一个命令产生输出的同时,输出会被立即送给第二个命令,数据传输不会用到任何中间文件或缓冲区。
[root@izwz9194nuv8g0cwqfqsh3z ~]# ps -ef | grep bio
root 18 2 0 Feb26 ? 00:00:00 [bioset]
root 19 2 0 Feb26 ? 00:00:00 [bioset]
root 20 2 0 Feb26 ? 00:00:00 [bioset]
root 26627 26551 0 16:35 pts/0 00:00:00 grep --color=auto bio
可以将管道输出>重定向给文件。
Bourne shell提供的特别命令expr
[root@izwz9194nuv8g0cwqfqsh3z ~]# expr 4 \* 2
8
[root@izwz9194nuv8g0cwqfqsh3z ~]# expr 4 + 2
6
[root@izwz9194nuv8g0cwqfqsh3z ~]# expr 4 - 2
2
[root@izwz9194nuv8g0cwqfqsh3z ~]# expr 4 / 2
2
bash shell为了与Bourne shell兼容而包含expr命令,但还提供了更简单的方法执行数学运算,不过只支持整数运算。
$[ operation ]
[root@izwz9194nuv8g0cwqfqsh3z ~]# var1=$[1 + 5]
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo $var1
6
[root@izwz9194nuv8g0cwqfqsh3z ~]# var2=$[$var1 * 2]
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo $var2
12
可以直接在命令行执行计算,也可以用在脚本,不用\进行转义,很方便
bash采用内建的bash计算器(又称bc)来解决浮点计算问题。
bc实际上是一种编程语言,允许在命令行中输入浮点表达式,然后解释并计算该表达式,最后返回结果。
其能够识别:
数字(整型和浮点)
变量(简单变量和数组)
注释(以#或/**/开始的行)
表达式
编程语言
函数
[root@izwz9194nuv8g0cwqfqsh3z ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
12*5.4 ##乘法
64.8
3.156*(5+6)
34.716
5.0/3 ##除法
1
scale=4 ##设置小数位
5/3
1.6666
var1=3 ##支持变量
var1*4
12
print var1 ##打印变量和数字
3
quit
在脚本中使用bc可以通过命令替换,基本格式为:
variable=$(echo "options; expression" | bc)
例如:
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test9
#!/bin/bash
var1=$(echo "scale=4; 3.44 / 5" | bc)
echo the answer is $var1[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test9
the answer is .6880
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test10
the answer for this is 2.2222
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test10
#!/bin/bash
var1=100
var2=45
var3=$(echo "scale=4;$var1 / $var2" | bc)
echo the answer for this is $var3
以上适合较短的运算,如果涉及大量运算,可以使用内联输入重定向。
variable=$(bc << EOF
options
statements
expressions
EOF
)
如
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test11
#!/bin/bash
var1=100
var2=45
var3=$(bc << EOFscale=4
a1=var1 * 3
a2=var2 / 5
a1 + a2
EOF
)
echo the answer for this is $var3
shell中运行的每个命令都使用退出状态码告诉shell其已经运行完毕。退出状态码是0-255之间的整数值。
Linux专门提供了变量 $? 来保存上一个已经执行命令的退出状态码
[root@izwz9194nuv8g0cwqfqsh3z ~]# date
Wed Mar 6 18:56:06 CST 2019
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo $?
0
正确退出状态码为0
如果有错误时会输出错误码
0:命令成功结束
1:一般性未知错误
2:不适合的shell命令
126:命令不可执行
127:没找到命令
128:无效的退出参数
128+x:与Linux信号x相关的严重错误
130:通过Ctrl+C终止的命令
255:正常范围之外的退出状态码
正常的shell脚本执行完之后的状态码是其最后一个命令的退出状态码,一般为0,不过我们可以通过exit命令修改退出码
exit num
exit $var
如在脚本最后一句使用exit命令
[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test4
the man's name is Mike
the user's name is root
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo $?
0 ##正常输出0
[root@izwz9194nuv8g0cwqfqsh3z ~]# vim test4
[root@izwz9194nuv8g0cwqfqsh3z ~]# cat test4
#!/bin/bash
var=Mike
result=`echo "the man's name is ${var}"`
echo ${result}
result=`echo "the user's name is ${USER}"`
echo ${result}
exit 6 ##增加exit命令[root@izwz9194nuv8g0cwqfqsh3z ~]# ./test4
the man's name is Mike
the user's name is root
[root@izwz9194nuv8g0cwqfqsh3z ~]# echo $?
6 ##按照我们设定的输出