Liunx学习笔记 - 07 - 03 shell script(shell脚本)
1 何为shell script
shell script(程序化脚本)是利用shell的功能所写的一个程序,这个程序使用纯文本文件,将一些shell的语法和命令(含外部命令)写在里面,搭配正则表达式、管道命令与数据流重定向等功能,达到我们想要的处理目的。
所有的编程语言都是由Hello World引入的,所以写个Hello World的shell脚本看看。
vim sh01_HelloWorld.sh # vim新建并编辑sh01_HelloWorld.sh文件
敲入该文件内容如下:
#!/bin/bash
# Program:
# This program shows "Hello World!" in your screen.
# History:
# 2019/12/30 by Mei Guanhua
echo "Hello World!"
exit 0
说明:
第一行#!/bin/bash说明该script使用的shell类型为bash,这一行必不可少,默认写法;
后面#开头的为注释行;
echo "Hello World!"为主命令,即向屏幕输出Hello World!字符串;
最后一行exit 0的意思是结束程序,并输出个0,这个0给到了环境变量?里,即表明程序正确执行了,当然,也可以用exit 非零数值来定义特定的错误类型。
写好该文件后,wq!保存退出vim编辑器即可。
如何运行该脚本呢?可以:
sh sh01_HelloWorld.sh # sh命令来执行该脚本,执行结果:
Hello World!
或
chmod 755 sh01_HelloWorld.sh # 先把该脚本权限改成能执行x的
./sh01_HelloWorld.sh # 直接执行就好了,执行结果:
Hello World!
2 简单范例
例1-交互式脚本,键盘读入用户姓名并输出到屏幕
#!/bin/bash
# Program:
# This program reads users' name from keybord, and then output them.
# History:
# 2019-12-30 Meiguanhua
read -p "Please input your first name: " firstname # 提醒用户输入名
read -p "Please input your last name: " lastname # 提醒用户输入姓
echo "\nYour full name is: $firstname $lastname" # 屏幕输出用户名和姓
exit 0
例2-简单的数值运算
#!/bin/bash
# Program:
# This program reads 2 integers and multiply them.
# History:
# 2019/12/30 by Mei Guanhua
echo -e "Please input two integers, I will multiply them."
read -p "first number: " firstNum
read -p "second number: " secondNum
total=$(($firstNum*$secondNum))
echo "$firstNum * $secondNum = " $total
exit 0
实际上,还可以用source XXX.sh来执行脚本,其和sh XXX.sh的区别是,sh的执行方式执行后,脚本中的变量为局部变量,而source执行后,脚本中的变量使全局变量(环境变量),其不会消失。
3 判断式
3.1 test命令的测试功能
test可以检测文件是否存在及其相关属性,比如要检查一个文件存在否,可以:
test -e fileName # 检测fileName文件存在否
可惜没有任何返回和显示,其实存在?变量里了,可以
test -e fileName && echo "exist" || echo "not exist"
测试标识 | 代表意义 |
---|---|
存在及类型 ,例 test -e fileName 表示是否存在 | |
-e | 该文件名是否存在 |
-f | 该文件名是否存在且为文件 |
-d | 该文件名是否存在且为目录 |
-b | 该文件名是否为一个blcok device设备 |
-c | 该文件名是否为一个character device设备 |
-S | 该文件名是否为一个Socket文件 |
-p | 该文件名是否为一个FIFO(pipe)文件 |
-L | 该文件名是否为一个链接文件 |
权限测试,例 test -r fileName表示是否可读 | |
-r | 检测该文件名是否存在且具有“可读”的权限 |
-w | 检测该文件名是否存在且具有“可写”的权限 |
-x | 检测该文件名是否存在且具有“可执行”的权限 |
-u | 检测该文件名是否存在且具有“SUID”的权限 |
-g | 检测该文件名是否存在且具有“SGID”的权限 |
-k | 检测该文件名是否存在且具有“Sticky bit”的权限 |
-s | 检测该文件名是否存在且为“非空白文件” |
两文件之间的比较,例 test file1 -nt file2 | |
-nt | (newer than)判断file1是否比file2新 |
-ot | (older than)判断file1是否比file2旧 |
-ef | 判断file1与file2是否为同一文件,可用来判断hard link硬链接,即判断两文件是否为同一inode |
两个整数间的判断,例 test n1 -eq n2 | |
-eq | 两数值相等(equal) |
-ne | 两数值不等(not equal) |
-gt | n1大于n2(greater than) |
-lt | n1小于n2(less than) |
-ge | n1大于等于n1(greater than or equal) |
-le | n1小于等于n2(less than or equal) |
判定字符串的数据 | |
test -z string | 判定字符串是否为0,若为空字符串,则为true |
test -n string | 判定字符串视为非0,若为空字符串,则为false,-n可省略 |
test str1=str2 | 判定str1是否等于str2,相等则为true |
test str1!=str2 | 判定str1是否不等于str2,相等则为false |
多重条件判断,例:test -r filename -a -x filename | |
-a | 两个条件同时成立 |
-o | 任何一个条件成立 |
! | 反向状态,例test ! -x file,则file不具有x权限为true |
例3-判断文件是否存在及其权限:
#!/bin/bash
# Program:
# Judge a file type.
# History:
# 2019-12-30 Mei Guanhua
echo "Please input a filename, then I will check its type and permissions. \n\n"
# 让用户输入文件名,并且判断该文件名是否为空,若为空,则直接跳出脚本
read -p "Please input a filename: " filename
test -z $filename && echo "You MUST input a filename!!" && exit 1
# 判断文件是否存在,若不存在则显示信息并结束脚本
test ! -e $filename && echo "The filename '$filename' DO NOT exist!!" && exit 2
# 判断文件属性和类型
test -f $filename && filetype="regular file"
test -d $filename && filetype="directory"
test -r $filename && perm="readable"
test -w $filename && perm="$perm writable"
test -x $filename && perm="$perm executable"
# 输出文件信息
echo "The filename: $filename is a $filetype ."
echo "Its permissions are: $perm ."
exit 0
3.2 利用判断符号[]
与test语法相同,还可以用中括号[]来表示数据的判断,例如判断变量myName是否为空,可以:
[ -z "$myName" ]; echo $?
注意,使用中括号时,中括号两端必须要有空格符来分隔,且中括号内的每个部分都要用括号来分隔,括号内的常量最好都用单引号或者双引号包括起来。
例4-根据用户输入的y/Y或n/N来显示yes或no:
#!/bin/bash
# Program
# This program shows the choice of user.
# History
# 2019-12-30 Mei Guanhua
read -p "Please input (Y/N): " yn
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue!" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "OK, interrupt!" && exit 0
echo "I don't know what your choice is. " && exit 1
3.3 shell script的默认变量($0、$1)
实际上,脚本执行的时候,可以在后面跟上变量,就跟咱们有时候写程序,函数名后面跟一堆输入参数一样的,那么如何在脚本中来使用这些变量呢?shell对这些变量有默认的名字,即脚本文件名为变量$0,第1个参数为$1,第2个参数为$2,以此类推。
此外,还有几个特殊的变量:
- $#表示后接参数的个数
- $@表示"$1" “$2” “$3”,每个变量用双引号括起来
- $*表示"$1 $2 $3",即用空格间隔开的每个变量
- $@和$*还是略有区别的,但是一般用$@就好了。
例5-显示shell script的默认变量:
#!/bin/bash
# Program:
# This program test the script name, parameters...
# History:
# 2019-12-30 Mei Guanhua
echo "The script name is $0"
echo "Total parameters is $#"
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. Stop here." \
&& exit 0
echo "Your whole parameter is '$@'"
echo "The 1st parameter is $1"
echo "The 2nd parameter is $2"
测试及结果
./sh07_scriptName.sh var1 var2 # 运行该脚本,后跟俩参数var1和var2,显示如下
The script name is ./sh07_scriptName.sh
Total parameters is 2
Your whole parameter is 'var1 var2'
The 1st parameter is var1
The 2nd parameter is var2
还可以用shift来使变量做偏移操作,其实就是向左卡掉几个变量的意思
例6-偏移变量
#!/bin/bash
# Program:
# This program test the effect of shit function.
# History:
# 2019-12-30 Mei Guanhua
echo "Total parameter number is $#"
echo "Your whole parameter is '$@'"
shift
echo "Total parameter number is $#"
echo "Your whole parameter is '$@'"
shift 3
echo "Total parameter number is $#"
echo "Your whole parameter is '$@'"
测试及运行结果
./sh08_shiftTest.sh var1 var2 var3 var4 var5 # 测试变量偏移,后跟5个变量,显示如下:
Total parameter number is 5
Your whole parameter is 'var1 var2 var3 var4 var5'
Total parameter number is 4
Your whole parameter is 'var2 var3 var4 var5'
Total parameter number is 1
Your whole parameter is 'var5'
4 条件判断式
跟所有的编程语言一样,也提供了if类型的判断结构和case类型的判断结构。
4.1 利用if…then
单重判断的语法:
if [ 条件判断式 ]; then
条件判断式成立时,进行的操作;
fi # 把if反过来写表示if的结束
多重判断的语法:
if [ 条件判断式 ]; then
条件判断式成立时,进行的操作;
else
条件判断式不成立时,执行的操作;
fi
更多嵌套分支的语法:
if [ 条件判断式1 ]; then
条件判断式1成立时,进行的操作;
elif [ 条件判断式2 ]; then
条件判断式1不成立,且,条件判断式2成立时,执行的操作;
else
条件判断式1和2都不成立,执行操作;
fi
条件判断式的写法和3.2小节的一样,如果有多个条件的组合,可以把它们放到一个中括号里,用-a、-o连接,或者把每个条件放到一个中括号里,然后用逻辑运算符号将其连接起来,&& 表示与,|| 表示或。
例7-根据用户输入的y/Y或n/N来输出yes或no,用if…then实现:
#!/bin/bash
# Program
# This program shows the choice of user.
# History
# 2019-12-30 Mei Guanhua
read -p "Please input (Y/N): " yn
if [ "$yn" == "Y" -o "$yn" == "y" ]; then
echo "OK, continue!"
exit 0
elif [ "$yn" == "N" ] || [ "$yn" == "n" ]; then
echo "OK, interrupt!"
exit 0
else
echo "I don't know what your choice is. "
exit 0
fi
4.2 利用case…esac
这个类似C++里面的switch,可以多重分支判断,判断变量吻合哪项内容就去做哪部分的操作,如果哪部分都不符合那就执行默认部分的操作,语法:
case $变量名 in
"第1个变量内容")
程序段
;;
"第2个变量内容")
程序段
;;
*)
exit 1
;;
esac
例8-如果用户输入和参数hello,则回复Hello,不然就提示输入字符串:
#!/bin/bash
# Program
# Check $1 is equal to "hello".
# History
# 2019-12-30 Mei Guanhua
case $1 in
"hello")
echo "Hello, how are you?"
;;
"")
echo "You MUST input parameters, ex> {$0 someword}"
;;
*)
echo "ex > {$0 hello}"
;;
esac
4.3 function功能
子函数也是编程语言所必须的,bash同样提供该功能,语法:
function funName() {
程序段
}
注意,函数一定要放在调用前面。
例9-让用户选择输入one、two、three:
#!/bin/bash
# Program
# Use function to repeat information.
# History
# 2019-12-30 Mei Guanhua
function printit() {
echo "Your choice is $1" # 注意这里的$1为function执行时的默认第1个变量值,也就是下面的1、2、3
}
echo "This program will print your selection!"
case $1 in # 注意这里的$1为该脚本调用时输入的第1个变量值,即用户输入第1个参数值
"one")
printit 1
;;
"two")
printit 2
;;
"three")
printit 3
;;
*)
echo "Usage $0 {one|two|three}"
;;
esac
5 循环loop
循环也是必不可少的,同样提供有while和for型循环
5.1 while do done,until do done(不定循环)
语法:
while [ condition ]
do
程序段; # 条件满足时执行动作
done
until [ condition ]
do
程序段; # 执行该动作直到condition满足时退出循环
done
两个比较类似,以吃饭为例,前者是我饿着呢所以就一直吃,后者是我吃啊吃直到吃饱为止。
例10-用户要是不给我yes/YES我就让他一直输:
#!/bin/bash
# Program
# Repeat question untile user input correct answer.
# History
# 2019-12-30 Mei Guanhua
while [ "$yn" != "yes" -a "$yn" != "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! You input the correct answer."
例11-用户不断输入直到输了yes/YES为止:
#!/bin/bash
# Program
# Repeat question untile user input correct answer.
# History
# 2019-12-30 Mei Guanhua
until [ "$yn" == "yes" -o "$yn" == "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! You input the correct answer."
例12-从1加到100:
#!/bin/bash
# Program
# This program add 1 to 100 using while loop
# History
# 2019-12-30 Mei Guanhua
s=0
i=0
while [ "$i" != "100" ]
do
i=$(($i+1))
s=$(($s+$i))
done
echo "The result of '1+2+3+...+100' is $s"
5.2 for do done(固定循环)
语法:
for var in con1 con2 con3 ...
do
程序段;
done
第1次循环式,$var的值为con1;
第2次循环式,$var的值为con2;
第3次循环式,$var的值为con3;
……
例13-输出动物园里已有的动物种类:
#!/bin/bash
# Program
# Using for loop to print 3 animals
# History
# 2019-12-30 Mei Guanhua
for animal in dog horse elephant
do
echo "There are ${animal}s..."
done
5.3 for do done的数值处理
语法:
for (( 初始值; 限制值; 终止步长 ))
do
程序段;
done
例14-从1加到n:
#!/bin/bash
# Program
# This program add 1 to 100 using for loop
# History
# 2019-12-30 Mei Guanhua
read -p "Please input a number, I will calculate 1+2+3+...+your_input: " nu
s=0
for ((i=1; i<=$nu; i=i+1))
do
s=$(($s+$i))
done
echo "The result of '1+2+3+...+$nu' is $s"