linux学习12 shell脚本

目录

1.简介

编写的第一个脚本

一个良好的编写习惯

2.shell脚本练习

简单范例

交互式脚本

随日期变化

数值运算

脚本执行方式差异

3.判断式

test命令

判断符号[ ]

shell脚本的默认变量 $0 $1 ...

条件判断式

利用case...esac判断

利用function功能

4.循环 loop

不定循环 while do done 与 until do done

for...do...done 固定循环

for...do...done 的数值处理

随机数与数组实验

5.shell脚本的跟踪与调试


1.简介

可以将shell脚本看成是一个程序,但它始终是一个由命令构成的纯文本文件,在系统管理上是一个非常好用的工具,但是用在大量数值运算上就有点力不从心了

在shell脚本的编写中需要注意的是

  • 命令是从上往下、从左往右分析与执行
  • 命令选项与参数之间的多个空格会被忽略
  • 空白行会被忽略,tab产生的空白视为空格键
  • 读到一个enter符就会尝试执行该行命令
  • 可使用\enter来扩展到下一行
  • #为注释符,不会执行该行后面的命令

执行脚本文件的几个方法

  • 直接命令执行:文件需要有rx权限
  • 绝对路径执行
  • 相对路径执行
  • 放入PATH变量
  • 以bash程序执行

编写的第一个脚本

#!/bin/bash
#Program:
#	This program shows 'hello world' in your screen.
#History:
#2022/8/23	zjw	First realease
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0

PATH变量可手动敲上去,也可以利用下面的方法快速写入

:! echo $PATH >> hello.sh

1.第一行 #!/bin/bash 声明该脚本使用的shell名称

此行被称为shebang行,告诉了系统该脚本使用什么shell来执行

2.程序内容说明

除了第一行#外其他的#都为注释,强烈建议要养成写脚本说明的习惯包括:

内容与功能;版本信息;作者与联系方式;建立日期;历史记录

3.主要环境变量的声明

务必设置好相关环境变量如PATH与LANG

4.写好主程序

5.定义返回值

一个良好的编写习惯

在每个脚本的文件头处记录好

脚本功能;脚本版本信息;作者与联系方式;脚本的版权声明;历史记录;脚本内特殊的命令;脚本运行时需要的环境变量预先声明与设置

脚本编写尽量美观

2.shell脚本练习

简单范例

交互式脚本

让用户自己输入一些内容从而让脚本顺利运行

#!/bin/bash
#Program:
#	This program shows your name.
#History:
#2022/8/23	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input your first name:" firstname
read -p "Please input your last name:" lastname
echo -e "\nYour full name is:${firstname}${lastname}"
利用 read -p 来提示用户输入

随日期变化

自动创建随日期变化的文件

#!/bin/bash
#Program:
#	按照不同的日期创建三个文件
#History:
#2022/8/23	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input your filename:" fileuser
filename=${fileuser:-"filename"}    #如果不输入filename直接回车,那文件名前缀就是filename
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date --date='1 days ago' +%Y%m%d)
date3=$(date +%Y%m%d)    #日期变量
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}    最终确定的文件变量,用于将文件名前缀与日期后缀结合
touch "${file1}"
touch "${file2}"
touch "${file3}"    #创建文件命令

数值运算

需要用declare自定义变量类型

#!/bin/bash
#Program:
#	实现数值的加减乘除计算
#	除法为整型除法
#History:
#2022/8/24	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
echo -e "Input two numbers."
read -p "First number:" firstnu
read -p "Second number:" secondnu
add=$((${firstnu}+${secondnu}))
sub=$((${firstnu}-${secondnu}))
mul=$((${firstnu}*${secondnu}))
div=$((${firstnu}/${secondnu}))
echo -e "${firstnu}+${secondnu}=${add}\n${firstnu}-${secondnu}=${sub}\n${firstnu}*${secondnu}=${mul}\n${firstnu}/${secondnu}=${div}"

数值运算还可以替换成下方写法,不过上方写法更容易记住

declare -i add=${firstnu}+${secondnu}

利用 bc 命令(bc为计算器命令,可进入命令行也可利用管道,第四章有说明)可以实现含有小数的运算

#!/bin/bash
#Program:
#	用户输入小数位数用以显示pi
#History:
#2022/8/24	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "The scale number(10-10000):" checking
num=${checking:-"10"}    #判断是否有输入数值,若直接回车,则默认10
echo -e "Starting calcuate pi values.Be patient..."
time echo "scale=${num}; 4*a(1)" | bc -lq    #time显示用时,4*a(1)是bc提供的计算pi的函数

脚本执行方式差异

直接执行脚本 sh filename.sh 其实是通过子进程执行,子进程产生的变量不会传递到父进程

利用 source filename.sh 会直接在父进程中执行,脚本结束后变量仍然存在

3.判断式

test命令

如果想要检查某个文件是否存在,可以利用下面的命令查看,返回值需要通过echo $?来查看

test -e filename

 test的所有参数在P396

判断符号[ ]

[zjw@zjw ~]$ [ -z $HOME ] ; echo $?
用于判断变量是否为空,非空则返回1

中括号内的每个组件都需要有空格来分隔

中括号内的变量最好都以双引号括起来

中括号内的常数最好都以单或双引号括起来

[zjw@zjw ~]$ name="zjw zjw"
[zjw@zjw ~]$ [ $name="abc" ]
bash: [: zjw: unary operator expected
$name不用双引号括起来,就会默认为 [ zjw zjw="abc" ]
#!/bin/bash
#Program:
#	This program shows the user's choice.
#History:
#2022/8/26	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input (Y/N):" yn
[ "${yn}"=="Y" -o "${yn}"=="y" ] && echo "Continue" && exit 0    #-o(或)用于连接两个判断
[ "${yn}"=="N" -o "${yn}"=="n" ] && echo "Interrupt" && exit 0
echo "I don't know what your choice is" && exit 0

shell脚本的默认变量 $0 $1 ...

利用变量可以实现在脚本文件名后面携带参数

/path/to/scriptname        opt1        opt2        ...

                        $0                $1                $2

还有一些特殊变量可以在脚本内使用来调用上述参数

$#后接参数个数
$@代表 "$1" "$2" "$3",即每个独立的变量
$*代表 "$1c$2c$3",c为分隔符,默认空格
#!/bin/bash
#Program:
#	程序显示文件名、参数个数...
#History:
#2022/8/26	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
echo "The script name is:${0}"
echo "Total parameter number is:$#"    #显示了所有参数个数
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2." && exit 0
echo "Your whole parameter is:$@"    #列出每个独立变量
echo "The 1st parameter:${1}"
echo "The 2nd parameter:${2}"

变量的偏移

#!/bin/bash
#Program:
#	变量的偏移。
#History:
#2022/8/26	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
echo "Total parameter number is:$#"
echo "Whole parameter is:$@"
shift    #第一次偏移
echo "Total parameter number is:$#"
echo "Whole parameter is:$@"
shift 3    #第二次偏移3个
echo "Total parameter number is:$#"
echo "Whole parameter is:$@"

[zjw@zjw bin]$ sh shift_paras.sh 1 2 3 4 5
Total parameter number is:5
Whole parameter is:1 2 3 4 5
Total parameter number is:4
Whole parameter is:2 3 4 5
Total parameter number is:1
Whole parameter is:5

条件判断式

单层条件判断式

#!/bin/bash
#Program:
#	This program shows user's choice.
#History:
#2022/8/29	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input(Y/N):" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ];then 
	echo "Continue"
	exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ];then     #注意中括号两边及等号两端的空格,否则会报错
	echo "Interrupt"
	exit 0
fi
echo "I don't know" && exit 0

多重条件判断式

#!/bin/bash
#Program:
#	This program shows user's choice.
#History:
#2022/8/29	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input(Y/N):" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ];then 
	echo "Continue"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ];then
	echo "Interrupt"
else
	echo "I don't know"
fi

#!/bin/bash
#Program:
#	判断$1是否为hello
#History:
#2022/8/29	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
if [ "${1}" == "hello" ];then
	echo "yes"
elif [ "${1}" == "" ];then
	echo "You must input parameters,ex> [${0} parameters]"
else
	echo "no"
fi

利用case...esac判断

case后的变量有两种获取方式,直接执行方式与交互式

直接执行获得变量
#!/bin/bash
#Program:
#	Show "hello" from $1... by using case...esac
#History:
#2022/8/31	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
case ${1} in
	"hello")
		echo "Hello, how are you ?"
		;;
	"")
		echo "You MUST input parameters, ex> {${0} someword}"
		;;
	*)
		echo "Usage ${0} {hello}"
		;;
esac

交互式获得变量
#!/bin/bash
#Program:
#	This script only accepts the flowing parameter: one, two, three.
#History:
#2022/8/31	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Input your choice:" choice
case ${choice} in
	"one")
		echo "Your choice is one"
		;;
	"two")
		echo "Your choice is two"
		;;
	"three")
		echo "Your choice is three"
		;;
	*)
		echo "Usage ${0} {one|two|three}"
		;;
esac
区别在于定义一个新的变量来获得用户的输入

利用function功能

shell脚本的执行方式是从上而下从左到右,所以函数的定义一定要在程序前面

函数内部的${1}与shell脚本内的${1}是不一样的

#!/bin/bash
#Program:
#	This script only accepts the flowing parameter: one, two, three.
#History:
#2022/8/31	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
function printit()
{
	echo -n "Your choice is "
}
echo "This prigram will print your selection"
case ${1} in
	"one")
		printit; echo ${1} | tr 'a-z' 'A-Z'
		;;
	"two")
		printit; echo ${1} | tr 'a-z' 'A-Z'
		;;
	"three")
		printit; echo ${1} | tr 'a-z' 'A-Z'
		;;
	*)
		echo "Usage ${0} {one|two|three}"
		;;
esac

4.循环 loop

不定循环 while do done 与 until do done

#!/bin/bash
#Program:
#	Repeat question until user input correct answer.
#History:
#2022/8/31	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input yes or no:" ans
while [ "${ans}" != "yes" -a "${ans}" != "YES" ]
do
	read -p "Please input yes/no to stop this program:" ans
done
echo "OK, correct answer"

#!/bin/bash
#Program:
#	Repeat question until user input correct answer.
#History:
#2022/8/31	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input yes or no:" ans
until [ "${ans}" == "yes" -a "${ans}" == "YES" ]    #while与until的区别在于条件判断的不同
do
	read -p "Please input yes/no to stop this program:" ans
done
echo "OK, you input the correct answer"
#!/bin/bash
#Program:
#	Use loop to calculate 1+2+...+input
#History:
#2022/8/31	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
sum=0
i=0
read -p "Please input a number:" num
while [ "${i}" != "${num}" ]    #这里的双引号一定要写对,且 <= 无法识别
do
	i=$((${i}+1))
	sum=$((${sum}+${i}))
done
echo "The result is: ${sum}"

for...do...done 固定循环

for var in con1 con2 con3...

do

        程序

done

第一次循环,变量var的内容为con1,第二次循环变量var的内容为con2...

#!/bin/bash
#Program:
#	Using loop to print three animals.
#History:
#2022/9/1	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
for animals in dogs cats elephants
do
	echo "There are ${animals}"
done

若需要连续输出,则需要用到命令 seq

#!/bin/bash
#Program:
#	Using ping command to check the network's PC state.
#History:
#2022/9/1	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
network="192.168.1"
for sitenu in $(seq 1 100)    # $(seq 1 100) 即从1到100,也可以用{1..100}来替换
do
	ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
	if [ "${result}"==0 ]; then
		echo "Server ${network}.${sitenu} is UP."
	else
		echo "Server ${network}.${sitenu} is DOWN."
	fi
done

判断加上循环

#!/bin/bash
#Program:
#	User input dir name, I find the permission of files.
#History:
#2022/9/1	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
read -p "Please input a directory:" dir
if [ "${dir}" == "" -o ! -d "${dir}" ]; then    #条件判断,如果是空目录或者不是目录 -o 是或者的意思;! -d "${dir}" 即判断是否为目录,不是目录则返回真
	echo "The ${dir} is NOT exist in your system."
	exit 1
fi
filelist=$(ls ${dir})    #用以输出目录内的文件
for filename in ${filelist}
do
	perm=" "
	test -r "${dir}/${filename}" && perm="${perm} readable"
	test -w "${dir}/${filename}" && perm="${perm} writable"
	test -x "${dir}/${filename}" && perm="${perm} executable"
	echo "This file ${dir}/$${filename}'s permission is ${perm}"
done

for...do...done 的数值处理

for (( 初始值; 限制值; 复制运算))

do

        程序段

done

适合用于数值运算的循环

  • 初始值:变量的初始值
  • 限制值:变量的值在这个限制值内变化,则继续循环
  • 复制运算:每做一次循环,变量也做相应的变化
#!/bin/bash
#Program:
#	Calculate 1+2+...+num
#History:
#2022/8/26	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
sum=0
read -p "Please input a number:" num
for (( i=0; i<=${num}; i=i+1 ))
do
	declare -i sum=${sum}+${i}
done
echo "The sum is ${sum}"

shell不支持向c中的i++等操作,所以只能i=i+1

随机数与数组实验

只输出一组数据

#!/bin/bash
#Program:
#	Try to tell you what you may eat.
#History:
#2022/9/1	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
eat[1]="馋嘴鱼"
eat[2]="烤鸭饭"
eat[3]="麻辣烫(晚)"
eat[4]="大碗面"
eat[5]="酥肉小锅"
eat[6]="炒饭"
eat[7]="香锅(晚)"
eatnum=7
check=$(( ${RANDOM} * ${eatnum} / 32767 + 1 ))    #重点在 ${RANDOM},变量产生的随机数范围在0-32676
echo "You may eat ${eat[${check}]}"

同时输出多组不重复的数据

#!/bin/bash
#Program:
#	Try to tell you what you may eat.
#History:
#2022/9/1	zjw	First release
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:~/bin
export PATH
eat[1]="馋嘴鱼"
eat[2]="烤鸭饭"
eat[3]="麻辣烫(晚)"
eat[4]="大碗面"
eat[5]="酥肉小锅"
eat[6]="炒饭"
eat[7]="香锅(晚)"
eatnum=7
eated=0
while [ "${eated}" -lt 2 ]; do
	check=$((${RANDOM}*${eatnum}/32676+1))
	mycheck=0
	if [ "${eated}" -ge 1 ]; then
		for i in $(seq 1 ${eated})
		do
			if [ ${eatedcon[$i]} == $check ]; then
				mycheck=1
			fi
		done
	fi
	if [ ${mycheck} == 0 ]; then
		echo "You may eat ${eat[${check}]}"
		eated=$((${eated}+1))
		eatedcon[${eated}]=${check}
	fi
done

5.shell脚本的跟踪与调试

每次跟着鸟哥写完脚本一运行,总是会出错,进去看了两三遍才发现变量写错了,但是通过bash的相关参数,可以不用执行脚本就可以检测出错误

sh [-nvx] scripts.sh

-n 不需要执行脚本,仅查询语法问题

-v 执行脚本前,先将脚本文件内容显示在屏幕上

-x 将使用到的脚本内容显示到屏幕上

如果没有语法问题,则不会显示信息

[zjw@zjw bin]$ sh -x mybirth.sh 
+ PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/home/zjw/bin
+ export PATH
+ read -p 'Please input your birthday(YYYYMMDD ex>20220831):' date1
Please input your birthday(YYYYMMDD ex>20220831):20220224
++ echo 20220224
++ grep '[0-9]\{8\}'
+ date_check=20220224
+ '[' 20220224 == '' ']'
++ date --date=20220224 +%s
+ declare -i date1_s=1645632000
++ date +%s
+ declare -i date_now=1662036001
+ declare -i date_total_s=-16404001
+ declare -i date_total=-189
+ '[' -16404001 -lt 0 ']'
+ echo 'You have had your birthday' 189 'days ago'
You have had your birthday 189 days ago
这是个很有用的参数,可以显示出脚本内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值