
【linux】shell脚本编程
《Unix&linux大学教程》中并未提及shell编程内容,以下内容来自《鸟哥的linux》书
创建sh文件
创建文件时,将后缀写成sh即可vim first.sh。
改成.sh只是为了方便vim辨识,在编写时对不同变量采用不同颜色
实际上,first程序加上可执行权限后,就可以直接运行,与后缀.sh无关
写第一个程序
shell脚本第一行要注明文件使用的语法,如bash。
当first.sh程序被执行时,就能加载bash相关环境配置文件,并用bash程序执行自己写的命令
#!/bin/bash
# program:
# 这里描述first.sh程序的功能
# author:lthero
# history:
# 这里记录修改时间
echo "hello world \n"
exit 0first.sh程序将输出”hello world“这句话。
并使用exit命令让程序停止,返回0给系统,表示程序运行成功。如果返回其它数值,可以表示错误信息。
让用户输入
#!/bin/bash
# program:
# second.sh让用户分别输入两个数字,并赋值给firstnum,secnum,计算结果给total后,输出结果
# author:lthero
# history:
# 这里记录修改时间
read -p "请输入第一个数字 :" firstnum
read -p "请输入第二个数字 :" secnum
#变量分别为firstnum和secnum
#需要引用时,使用下面这样的格式
total=$((${firstnum}+${secnum}))
echo "${firstnum}+${secnum}结果是${total}\n"
exit 0解释
- 调用变量要用
$变量名格式,如果在字符串内使用"${变量名}" - 创建变量时,直接写变量名即可,如
firstnum=111 或 firstring="my name is lthero" $()内可以计算式子,如$((13%3))、 $((13*3))。也可以执行命令,如:$(date),并将结果返回
相关参数
- -p表示:后面跟提示信息,即在输入前打印提示信息。
- -t后面跟秒数,定义输入字符的
等待时间,如果超过设置时间未输入,则返回0,用来给if判断
if read -t 5 -p "输入进程名:" processName
then
echo "你输入的进程名是 $processName"
else
echo "\n抱歉,你输入超时了。"
fi
exit 0- -n后跟一个数字,定义输入文本的长度,输入一个字符后,
无需按回车即可完成
read -n1 -p "Do you want to continue [Y/N]?" answer
case $answer in
Y | y)
echo "fine ,continue";;
N | n)
echo "ok,good bye";;
*)
echo "error choice";;- -s 选项能够使 read 命令中输入的数据
不显示在命令终端上(实际上,数据是显示的,只是 read 命令将文本颜色设置成与背景相同的颜色)。输入密码常用这个选项。
#!/bin/bash
read -s -p "请输入您的密码:" pass
echo "\n您输入的密码是 $pass"
exit 0- 读取文件
#!/bin/bash
count=1 # 赋值语句,不加空格
#每次调用 read 命令都会读取文件中的 "一行" 文本。当文件没有可读的行时,read 命令将以非零状态退出。
cat test.txt | while read line # cat 命令的输出作为read命令的输入,read从管道中读到的值放在line中
do
echo "Line $count:$line"
count=$[ $count + 1 ] # 注意中括号中的空格。
done
echo "finish"
exit 0
test判断命令
如果想要判断一个目录或文件是否存在,我们可以用ls结果grep查看,这里将使用更简单的方式test命令:为真返回true,否则返回false
判断文件类型
如test -e filename 表示是否存在-e 该【文件名】是否存在
-f 该【文件名】是否为文件(file)
-d 该【文件名】是否为目录(directory)
举例
下面会讲到if else用法,但使用test命令时,不需要添加括号[]
#!/bin/bash
read -p "输入要查询的文件名,判断是否存在" filename
if test -e ${filename}
then
printf "存在"
else
printf "不存在"
fi判断文件权限
如test -r filename 表示是否可读-r 检测【文件名】是否存在而且【可读】
-w 检测【文件名】是否存在而且【可写】
-x 检测【文件名】是否存在而且【可执行】
#!/bin/bash
read -p "输入要查询的文件名,判断是否存在" filename
if test -x ${filename}
then
printf "是可执行文件"
else
printf "不是可执行文件"
fi
printf "\n"结果
输入要查询的文件名,判断是否存在ckrun
是可执行文件两个整数比较
如test num1 -eq num2 ,比较是否相等-eq 两个数相等equal
-ne 两个数不相等not equal
-gt num1大于num2(greater than)
-ge num1大于等于num2(greater than or equal)
-lt num1小于num2(less than)
-le num1小于等于num2(less than or equal)
字符串比较
test str1 == str2是否相等
test str1 != str2是否不相等
向shell脚本传入参数
在运行一些服务时,如node app.js ,可以把node比作一个sh脚本,app.js作为一个参数。
系统对脚本添加参数已经有了规定:
first.sh opt1 opt2 opt3 opt4 opt5 将分别对应变量
$0 $1 $2 $3 $4 $5
脚本的路径为$0,第一个参数是$1,等等
如:新建一个脚本,vim sec.sh
#!/bin/bash
echo "脚本名字为 ${0}"
echo "第一个参数为 ${1}"
echo "第二个参数为 ${2}"
echo "第三个参数为 ${3}"
exit 0root@lthero:Test[759]$ bash sec.sh 666 777 888
脚本名字为 sec.sh
第一个参数为 666
第二个参数为 777
第三个参数为 888除此以外,还有
$#=> 代表传入的参数个数,不包含$0$@=> 代表全部参数,echo "$@"将输出 “666 777 888”$*=> 代表"$1c$2c$3c$4" 其中的c是分隔符,c默认为空格
判断语句
一共有3种类型:“只有if”,“if+else”,“if+else if+else”
1、只用if
if [ 条件判断1 -o 条件判断2 -a 条件判断3];then
条件成立后执行
fi #表示if句子结束
或着将then写在if的下一行也行
if [ 条件判断1 -o 条件判断2 -a 条件判断3]
then
条件成立后执行
fi #表示if句子结束
解释:
-o 与|| 代表或者
-a 与&& 代表并且
2、另外两种类型
如果使用|| &&时要改成以下形式
if [ 条件判断1 ] && [ 条件判断2 ];then
something
else if [ 条件判断3 ] || [ 条件判断4 ];then
something
else if [ 条件判断5 ] && [ 条件判断6 ];then
something
else
something
fi
注意:
- 判断符号**[]**两端需要有空格来分隔 [
语句] - fi只写在最结尾,如果有多个else if ,fi也写在最结尾
- if与else if后面要接
[条件]和then,else后直接接语句
函数
shell脚本支持函数编写
function foo(){
内容
}
与调用sh程序一样,调用函数时也能传递参数,并且也是按$1、$2……命名
注意:
如果传入shell有$1,给函数也传入$1,函数将使用函数接收的$1
#!/bin/bash
function foo(){
echo "输出函数中的第零个参数${0}"
echo "输出函数中的第一个参数${1}"
}
echo "输出函数外的第零个参数${0}"
echo "输出函数外的第一个参数${1}"
#调用函数,并传入参数666
foo 666
exit 0调用sh程序,发现函数中的$1变成了666
root@lthero:Test[764]$ bash 测试函数传入参数.sh 111
输出函数外的第零个参数测试函数传入参数.sh
输出函数外的第一个参数111
输出函数中的第零个参数测试函数传入参数.sh
输出函数中的第一个参数666注意:
如果调用时不给函数的参数$1,函数也不会调用传入shell的$1,如下
#!/bin/bash
function foo(){
echo "输出函数中的第零个参数${0}"
echo "输出函数中的第一个参数${1}"
}
echo "输出函数外的第零个参数${0}"
echo "输出函数外的第一个参数${1}"
#调用函数,不传入参数
foo
exit 0再次调用sh程序,$1没有内容
root@lthero:Test[766]$ bash 测试函数传入参数.sh 111
输出函数外的第零个参数测试函数传入参数.sh
输出函数外的第一个参数111
输出函数中的第零个参数测试函数传入参数.sh
输出函数中的第一个参数循环
for…do…done(固定循环)
增强for循环
#像python的语法
for each_animal in cat dog elephant
do
echo $each_animal#输出会自动换行
done
for循环的对象要求用空格分开即可,
如,将用户输入的aaa bbb ccc ddd 一行内容输出
#!/bin/bash
read -p "输入一串内容空格分开" arr
for i in $arr
do
echo $i
done
exit 0测试如下
root@lthero:Test[777]$ bash third.sh
输入一串内容空格分开aaa bbb ccc ddd
aaa
bbb
ccc
ddd指定循环次数
类c语法:for(初始值;限制条件;赋值)
for ((i=1; i<=100; i ++))
do
echo $i
done使用in
for i in {1..100}
do
echo $i
done使用seq
for i in $(seq 1 100)
do
echo $i
done注意:
- i无需提前声明
while do done
while循环,当条件成立时执行something
#当con不为yes而且不为YES时,用户将一直输入
while [ “
${con}” !=“yes” -a “${con}” !=“YES” ]do
read -p “请输入yes/YES,否则程序不会停止” con
done
until do done
until 循环,直到条件成立时才停止循环
#当con不为yes而且不为YES时,用户将一直输入
until [ “
${con}” ==“yes” -o “${con}” ==“YES” ]do
read -p “请输入yes/YES,否则程序不会停止” con
done
综合实验
现在,实现一个功能:输入几个端口,查看系统是否开放了这些端口
使用netstat -tuln 命令可以输出全部开放的端口,再结合grep 过滤
#!/bin/bash
#在foo中,$1为第一个参数,$2为第二个等等
function foo(){
tmpfile=./tmpfile
touch ${tmpfile}
$(netstat -tuln > ${tmpfile})
testing=$(grep ":\<${1}\>" ${tmpfile})
if [ "${testing}" != "" ];then
printf "%-4s 端口已经开放\n" "${1}"
echo $testing
echo ""
else
printf "%-4s 端口未开放\n\n" "${1}"
fi
rm ${tmpfile}
}
#如果cknet自带参数则检测参数,否则让用户输入端口
if [ $# -ne 0 ];then
for i in $@
do
foo $i
done
else
read -p "输入要查询的端口 多个端口用空格分开 :" port
if [ "${port}" == "" ];then
exit 0
fi
for i in $port
do
foo $i
done
fi为了将自己的sh像系统程序一样输入名字即可执行,还要一步操作:
把sh文件所在目录添加到PATH中,如我的程序路径为/home/lthero/myprogram/cknet,那么目录就是**/home/lthero/myprogram/**
添加方法:
方法1、在~/.bashrc中添加:PATH="$PATH:/home/lthero/myprogram:" ,保存并退出,随后执行source ~/.bashrc 立即生效
方法2、直接修改/etc/enviroment:在PATH最后加上/home/lthero/myprogram:(“:”也要)
运行测试
root@lthero:Test[762]$ cknet
输入要查询的端口80 5000 8888 888
80 正在启动
5000 正在启动
8888 正在启动
888 正在启动综合实验二
输入进程名,判断是否在运行,返回进程pid
#!/bin/bash
# program:ckrun 关键词,检测是否在运行
#在foo中,$1为第一个参数,$2为第二个等等
function foo(){
#tmpfile用来保存ps结果
tmpfile=./tmpfile
$(ps -A > $tmpfile)
testing=$(grep "${1}" $tmpfile)
if [ "${testing}" != "" ];then
printf "%-4s 进程在运行中\n" "${1}"
for i in $testing
do
printf "%s\t" "${i}"
done
printf "\n"
else
printf "%-4s 进程不运行\n" "${1}"
fi
rm ${tmpfile}
}
printf "PID\tTTY\tTIME\t\tCMD\n"
#如果ckrun自带参数则检测参数,否则让用户输入要查询的进程名
if [ $# -ne 0 ];then
for i in $@
do
foo $i
done
else
read -p "输入要查询的程序 多个程序用空格分开 :" port
if [ "${port}" == "" ];then
exit 0
fi
for i in $port
do
foo $i
done
fi运行结果
root@lthero:my_programs[641]$ ckrun ndoe
PID TTY TIME CMD
ndoe 进程不运行
root@lthero:my_programs[642]$ ckrun node
PID TTY TIME CMD
node 进程在运行中
3729576 ? 00:00:00 node 3729598 ? 00:00:34 node- 文章作者:
- Lthero博客
- 版权声明:
- 本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议。转载请注明来自 Lthero博客!





