[toc]
6-Shell命令脚本
一、Shell脚本
1、什么时Shell脚本
Shell
终端解释器,是人与计算机硬件之间的“翻译官”,它作为用户与Linux系统内部的通信媒介,除了能够支持各种变量与参数外,还提供了诸如循环、分支等高级编程语言才有的控制结构特性。
Shell
脚本命令的工作方式有两种:
- 交互式(
Interactive
):用户每输入一条命令就立即执行。 - 批处理(
Batch
):由用户事先编写好一个完整的Shell
脚本,Shell
会一次性执行脚本中诸多的命令。
Shell
脚本中用到linux
命令以及正则表达式、管道符、数据流重定向等语法规则,还需要把内部功能模块化后通过逻辑语句进行处理,最终形成日常所见的Shell
脚本。
2、编写简单的脚本
使用Vim
编辑器把Linux
命令按照顺序依次写入到一个文件中,就是一个简单的脚本。
脚本命名:名称.sh
Shell
脚本文件的名称可以任意,建议将.sh
后缀加上,以表示是一个脚本文件。
2.1、创建脚本
脚本格式:
#! 系统shell解析器说明 系统使用哪种`shell` 解析器来执行脚本
# 脚本功能与命令说明
命令行
命令行
...
示例2:创建一个简单脚本test.sh
,执行pwd
与ls
命令
#! /bin/bash
# 简单的脚本,执行输出当前目录路径和目录详情
pwd
ls -la
2.2、执行脚本
执行脚本有两种方式:
bash
命令直接运行:bash 脚本文件.sh
- 输入完整路径执行:
- 绝对路径:
/路径/脚本文件.sh
- 相对路径(相对脚本所在目录):./脚本文件.sh
- 使用此方式,需设置脚本文件执行权限
- 绝对路径:
示例1:使用bash
执行脚本
[root@localhost ~]# bash test.sh
/root
总用量 332
dr-xr-x---. 16 root root 4096 2月 12 16:29 .
dr-xr-xr-x. 18 root root 236 1月 27 16:24 ..
………………省略部分输出………………
示例2:使用路径执行
[root@localhost test]# ~/test.sh
bash: /root/test.sh: 权限不够
执行时权限不足,查看文件权限信息,文件针对所有用户没有可执行权限。
[root@localhost test]# ls -l ~/test.sh
-rw-r--r--. 1 root root 92 2月 12 16:29 /root/test.sh
改变文件权限,对所有用户授予可执行权限后,再次执行。
# 授予权限
[root@localhost test]# chmod 755 ~/test.sh
[root@localhost test]# ~/test.sh
总用量 16
drwxr-xr-x. 2 root root 64 2月 19 15:37 .
dr-xr-x---. 16 root root 4096 2月 19 16:29 ..
-rw-r--r--. 1 root root 0 2月 19 15:01 a.txt
-rw-r--r--. 1 root root 2310 2月 19 15:17 b.txt
-rw-r--r--. 1 root root 70 2月 19 15:22 index.html
-rw-r--r--. 1 root root 2307 2月 19 15:37 passwd
3、接收用户的参数
shell
脚本语言内设了用于接收参数的变量,变量之间可以使用空格间隔。
内设变量:
$0
:对应的是当前Shell
脚本程序的名称$#
:对应的是总共有几个参数$*
:对应的是所有位置的参数值$?
:对应的是显示上一次命令的执行返回值$1、$2、$3
:分别对应着第N个位置的参数值
示例:编写一个脚本example.sh
,引用变量参数
① 创建脚本
#! /bin/bash
echo "当前脚本名称为$0"
echo "中共有$#个参数,分别是$*。"
echo "第1个参数为$1,第5个为$5。"
② 执行脚本,并传入参数
[root@localhost test]# vim example.sh
[root@localhost test]# bash example.sh one two three four five six
当前脚本名称为example.sh
中共有6个参数,分别是one two three four five six。
第1个参数为one,第5个为five。
4、判断用户的参数
Shell
脚本中的条件测试语法可以判断表达式是否成立,若条件成立则返回数字0
,否则便返回其他随机数值。
条件测试语句分类:
- 文件测试语句
- 逻辑测试语句
- 整数值比较语句
- 字符串比较语句
4.1、文件测试语句
作用:测试文件是否存在或是否具有权限等语句
参数说明:
| 操作符 | 作用 |
| —— | ————————– |
| -d | 测试文件是否为目录类型 |
| -e | 测试文件是否存在 |
| -f | 判断是否为一般文件 |
| -r | 测试当前用户是否有权限读取 |
| -w | 测试当前用户是否有权限写入 |
| -x | 测试当前用户是否有权限执行 |
示例1:查看etc/fstab
是否是一个目录
执行命令后可使用$?
变量显示上一条命令执行的结果,返回0是目录,其它非0值不是目录。
[root@localhost test]# [ -d /etc/fstab ]
[root@localhost test]# echo $?
1 # 返回1表示/etc/fstab不是目录
示例2:查看etc/fstab
是否是一个文件
[root@localhost test]# [ -f /etc/fstab ]
[root@localhost test]# echo $?
0 # 返回1表示/etc/fstab是一个普通文件
4.2、逻辑测试语句
作用:逻辑语句用于对测试结果进行逻辑分析,根据测试结果可实现不同的效果。
逻辑运算符:
- 逻辑“与”:&&
- 逻辑“或”:||
- 逻辑“非”:!
示例1:判断/dev/cdrom
文件是否存在,若存在则输出存在
。
[root@localhost test]# [ -e /dev/cdrom ] && echo "存在"
存在
示例2:结合系统环境变量USER
来判断当前登录的用户是否为非管理员身份
[root@localhost test]# [ $USER = root ] || echo "非管理员"
[root@localhost test]# su tiger
[tiger@localhost test]$ [ $USER = root ] || echo "非管理员"
非管理员
示例3:切换回到root
管理员身份,再判断当前用户是否为一个非管理员的用户。由于判断结果因为两次否定而变成正确。
[root@localhost test]# [ $USER != root ] || echo "非管理员"
非管理员
示例4:结合系统环境变量USER
来判断当前登录的用户是否为管理员身份,如果是输入管理员,否则输出非管理员
[root@localhost test]# [ $USER = root ] && echo "管理员" || echo "非管理员"
管理员
[root@localhost test]# su tiger
[tiger@localhost test]$ [ $USER = root ] && echo "管理员" || echo "非管理员"
非管理员
4.3、整数值比较语句
作用:对数字进行比较操作,整数比较运算符仅是对数字的操作,不能将数字与字符串、文件等内容一起操作。
整数比较运算符:
| 操作符 | 作用 |
| —— | ————– |
| -eq | 是否等于 |
| -ne | 是否不等于 |
| -gt | 是否大于 |
| -lt | 是否小于 |
| -le | 是否等于或小于 |
| -ge | 是否大于或等于 |
示例1:10是否大于10以及10是否等于10
[tiger@localhost test]$ [ 10 -gt 10 ]
[tiger@localhost test]$ echo $?
1
[tiger@localhost test]$ [ 10 -eq 10 ]
[tiger@localhost test]$ echo $?
0
示例2:获取当前系统正在使用及可用的内存量信息,如果小于1024
则显示内存不足
① 使用free -m
命令查看内存使用量情况(单位为MB),然后通过grep Mem:
命令过滤出剩余内存量的行,再用awk '{print $4}'
命令只保留第四列,最后用FreeMem=`语句`的方式把语句内执行的结果赋值给变量。
[root@localhost test]$ free -m
total used free shared buff/cache available
Mem: 1819 848 91 15 879 789
Swap: 1023 7 1016
[root@localhost test]$ free -m |grep Mem:
Mem: 1819 848 90 15 879 788
[root@localhost test]$ free -m |grep Mem:| awk '{print $4}'
90
[root@localhost test]$ FreeMem=`free -m |grep Mem:| awk '{print $4}'`
[root@localhost test]$ echo $FreeMem
90
② 使用整数运算符来判断内存可用量的值是否小于1024
,若小于则会提示“内存不足”
[root@localhost test]$ [ $FreeMem -lt 1024 ] && echo "内存不足"
内存不足
4.4、字符串比较语句
作用:字符串比较语句用于判断测试字符串是否为空值,或两个字符串是否相同。它经常用来判断某个变量是否未被定义(即内容为空值)。
字符串比较运算符:
| 操作符 | 作用 |
| —— | ———————- |
| = | 比较字符串内容是否相同 |
| != | 比较字符串内容是否不同 |
| -z | 判断字符串内容是否为空 |
示例1:判断String
变量是否为空值,进而判断是否定义了这个变量
[root@localhost test]# [ -z $String ]
[root@localhost test]# echo $?
0 # 表示未定义变量
示例2:保存当前语系的环境变量值LANG
不是英语(en.US
)时,则会满足逻辑测试条件并输出“非英语”的字样
[root@localhost test]# [ $LANG != "en.US" ] && echo "非英语"
非英语
[root@localhost test]# echo $LANG
zh_CN.UTF-8
二、流程控制语句
使用linux
命令,管道符,参数以及条件判断语句可以编写一些简单的shell
脚本,但是这种脚本不适应与生产环境。它不能根据真实的工作需求来调整具体的执行命令,也不能根据某些条件实现自动循环执行。
例如,我们需要批量创建1000
位用户,首先要判断这些用户是否已经存在;若不存在,则通过循环语句让脚本自动且依次创建他们。流程控制语句可解决这个问题。
1、if条件测试语句
作用:if条件测试语句可以让脚本根据实际情况自动执行相应的命令。
if
语句分为:单分支结构
、双分支结构
、多分支结构
1.1、单分支结构
组成结构:if
、then
、fi
关键词组成
相当于口语中的“如果……那么……”。
示例:判断/media/cdrom
文件是否存在,若存在就结束条件判断和整个Shell
脚本,反之则去创建这个目录
① 创建mkcdrom.sh
脚本文件
#!/bin/bash
DIR="/media/cdrom"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi
② 执行脚本
[root@localhost test]# ls /media
[root@localhost test]# bash mkcdrom.sh
[root@localhost test]# ls -d /media/cdrom
/media/cdrom
[root@localhost test]#
1.2、双分支结构
组成结构:if
、then
、else
、fi
关键词组成
相当于口语中的“如果……那么……或者……那么……”。
示例:验证某台主机是否在线,然后根据返回值的结果,要么显示主机在线信息,要么显示主机不在线信息。
① 创建chkhost.sh
脚本文件
#!/bin/bash
ping -c 3 -i 0.2 $1 &> /dev/null
if [ $? -eq 0 ]
then
echo "主机 $1 在线"
else
echo "主机 $1 不在线"
fi
/dev/null
:“空”设备,也有人称它为黑洞。任何输入到这个“设备”的数据都将被直接丢弃。最常用的用法是把不需要的输出重定向到这个文件。
② 执行脚本
[root@localhost test]# bash checkhost.sh www.baidu.com
主机 www.baidu.com 在线
[root@localhost test]# bash checkhost.sh 192.168.163.139
主机 192.168.163.139 不在线
1.3、多分支结构
组成结构:if
、then
、elif
、else
、fi
关键词组成
相当于口语中的“如果……那么……如果……那么……”。
示例:判断用户输入的分数在哪个成绩区间内,然后输出如“优秀”、“及格”、“不及格”等提示信息。
① 创建chkscore.sh
脚本文件,分数大于等于85
分且小于等于100
分,输出“优秀”,大于等于70
分且小于等于84
分,输出“及格”,否则输入“不及格”
提示:Linux
系统中,read
是用来读取用户输入信息的命令,能够把接收到的用户输入信息赋值给后面的指定变量,-p
参数用于向用户显示一定的提示信息。
#!/bin/bash
read -p "请输入你的分数(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ] ; then
echo "$GRADE 是优秀的"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] ; then
echo "$GRADE 是及格的"
else
echo "$GRADE 是不及格的"
fi
② 执行脚本
[root@localhost test]# bash chkscore.sh
请输入你的分数(0-100):80
80 是及格的
[root@localhost test]# bash chkscore.sh
请输入你的分数(0-100):90
90 是优秀的
[root@localhost test]# bash chkscore.sh
请输入你的分数(0-100):60
60 是不及格的
2、for条件循环语句
作用:for
循环语句允许脚本一次性读取多个信息,然后逐一对信息进行操作处理。
语法格式:
示例1:使用for
循环语句从列表文件中读取多个用户名,然后为其逐一创建用户账户并设置密码。
① 创建用户名称列表文件users.txt
zhangsan
lisi
wangwu
② 编写脚本example.sh
,循环读取用户名称,使用id 用户名
判断用户是否存在,不存在创建,存在输出“用户已存在”
#! /bin/bash
read -p "请输入密码:" PASSWD
for USERNAME in `cat $1`
do
id $USERNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "用户已存在"
else
useradd $USERNAME &> /dev/null
echo "$PASSWD" | passwd --stdin $USERNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "$USERNAME 用户创建成功"
else
echo "$USERNAME 用户创建失败"
fi
fi
done
③ 执行脚本,将users.txt
做为参数传入创建用户
[root@localhost test]# bash example.sh users.txt
请输入密码:1234
zhangsan 用户创建成功
lisi 用户创建成功
wangwu 用户创建成功
[root@localhost test]# tail -3 /etc/passwd
zhangsan:x:1002:1002::/home/zhangsan:/bin/bash
lisi:x:1003:1003::/home/lisi:/bin/bash
wangwu:x:1004:1004::/home/wangwu:/bin/bash
3、while条件循环语句
while
条件循环语句是一种让脚本根据某些条件来重复执行命令的语句。
作用:while
循环语句通过判断条件测试的真假来决定是否继续执行命令,若条件为真就继续执行,为假就结束循环。
语法格式:
综合示例:结合使用多分支的if条件测试语句与while条件循环语句,编写一个用来猜测数值大小的脚本guess.sh
① 编写脚本guess.sh
使用$RANDOM
变量来调取出一个随机的数值(范围为0~32767
),将这个随机数对1000
进行取余操作,并使用expr
命令取得其结果,再用这个数值与用户通过read
命令输入的数值进行比较判断。这个判断语句分为三种情况,分别是判断用户输入的数值是等于、大于还是小于使用expr命令取得的数值。
提示:获得随机数语法
PRICE=$(expr $RANDOM % 1000)
$RANDOM
:获取随机数的变量%
:是对随机数进行1000取余expr
:命令是获取取余后的结果。
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
COUNT=0 #计数变量
echo "商品实际价格为0-999之间,猜猜看是多少?"
while true
do
read -p "请输入您猜测的价格数目:" INT
let COUNT++ # 计数自加1
if [ $INT -eq $PRICE ]
then
echo "恭喜您答对了,实际价格是 $PRICE"
echo "您总共猜测了 $COUNT 次"
exit 0 # 推出循环
elif [ $INT -gt $PRICE ]
then
echo "太高了!"
else
echo "太低了!"
fi
done
② 执行脚本
[root@localhost test]# bash guess.sh
商品实际价格为0-999之间,猜猜看是多少?
请输入您猜测的价格数目:300
太高了!
请输入您猜测的价格数目:200
太低了!
请输入您猜测的价格数目:250
太低了!
请输入您猜测的价格数目:275
太高了!
请输入您猜测的价格数目:260
恭喜您答对了,实际价格是 260
您总共猜测了 5 次
4、case条件语句
作用:case
语句是在多个范围内匹配数据,若匹配成功则执行相关命令并结束整个条件测试;而如果数据不在所列出的范围内,则会去执行星号(*)中所定义的默认命令。
语法格式:
示例:编写脚本checkkeys.sh
,提示用户输入一个字符并将其赋值给变量KEY
,然后根据变量KEY
的值向用户显示其值是字母、数字还是其他字符。
① 编写checkkeys.sh
脚本
#!/bin/bash
read -p "请输入一个字符,并按Enter键确认:" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您输入的是 字母。"
;;
[0-9])
echo "您输入的是 数字。"
;;
*)
echo "您输入的是 空格、功能键或其他控制字符。"
esac
② 执行脚本
[root@localhost test]# bash checkkeys.sh
请输入一个字符,并按Enter键确认:y
您输入的是 字母。
[root@localhost test]# bash checkkeys.sh
请输入一个字符,并按Enter键确认:2
您输入的是 数字。
[root@localhost test]# bash checkkeys.sh
请输入一个字符,并按Enter键确认:/
您输入的是 空格、功能键或其他控制字符。
作业
1、一个完整的Shell脚本应该哪些内容?
答:应该包括脚本声明、注释信息和可执行语句(即命令)。
2、分别解释Shell脚本中$0与$3变量的作用。
答:在Shell脚本中,$0代表脚本文件的名称,$3则代表该脚本在执行时接收的第三个参数。
3、if条件测试语句有几种结构,最灵活且最复杂的是哪种结构?
答:if条件测试语句包括单分支、双分支与多分支等三种结构,其中多分支结构是最灵活且最复杂的结构,其结构形式为if…then…elif…then…else…fi。
4、for条件循环语句的循环结构是什么样子的?
答:for条件循环语句的结构为“for 变量名 in 取值列表 do 命令序列 done”,如图4-20所示。
5、若在while条件循环语句中使用true作为循环条件,那么会发生什么事情?
答:因条件测试值永久为true,因此脚本中循环部分会无限地重复执行下去,直到碰到exit命令才会结束。
6、如果需要依据用户的输入参数执行不同的操作,最方便的条件测试语句是什么?
答:case条件语句。