【linux】shell脚本编程
《Unix&linux大学教程》中并未提及shell编程内容,以下内容来自《鸟哥的linux》书
创建sh文件
创建文件时,将后缀写成sh即可vim first.sh
。
改成.sh只是为了方便vim辨识,在编写时对不同变量采用不同颜色
实际上,first程序加上可执行权限后,就可以直接运行,与后缀.sh无关
写第一个程序
shell脚本第一行要注明文件使用的语法,如bash。
当first.sh程序被执行时,就能加载bash相关环境配置文件,并用bash程序执行自己写的命令
1 | !/bin/bash |
first.sh程序将输出”hello world“这句话。
并使用exit命令让程序停止,返回0给系统,表示程序运行成功。如果返回其它数值,可以表示错误信息。
让用户输入
1 | !/bin/bash |
解释
- 调用变量要用
$变量名
格式,如果在字符串内使用"${变量名}"
- 创建变量时,直接写变量名即可,如
firstnum=111 或 firstring="my name is lthero"
$()
内可以计算式子,如$((13%3))、 $((13*3))
。也可以执行命令,如:$(date)
,并将结果返回
相关参数
- -p表示:后面跟提示信息,即在输入前打印提示信息。
- -t后面跟秒数,定义输入字符的
等待时间
,如果超过设置时间未输入,则返回0,用来给if判断
1 | if read -t 5 -p "输入进程名:" processName |
- -n后跟一个数字,定义输入文本的长度,输入一个字符后,
无需按回车即可完成
1 | read -n1 -p "Do you want to continue [Y/N]?" answer |
- -s 选项能够使 read 命令中输入的数据
不显示在命令终端上
(实际上,数据是显示的,只是 read 命令将文本颜色设置成与背景相同的颜色)。输入密码常用这个选项。
1 | !/bin/bash |
- 读取文件
1 | !/bin/bash |
test判断命令
如果想要判断一个目录或文件是否存在,我们可以用ls结果grep查看,这里将使用更简单的方式test命令:为真返回true,否则返回false
判断文件类型
如
test -e filename
表示是否存在
-e 该【文件名】是否存在
-f 该【文件名】是否为文件(file)
-d 该【文件名】是否为目录(directory)
举例
下面会讲到if else用法,但使用test命令时,不需要添加括号[]
1 | !/bin/bash |
判断文件权限
如
test -r filename
表示是否可读
-r 检测【文件名】是否存在而且【可读】
-w 检测【文件名】是否存在而且【可写】
-x 检测【文件名】是否存在而且【可执行】
1 | !/bin/bash |
结果
1 | 输入要查询的文件名,判断是否存在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
1 | !/bin/bash |
1 | root@lthero:Test[759]$ bash 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
1 | !/bin/bash |
调用sh程序,发现函数中的$1变成了666
1 | root@lthero:Test[764]$ bash 测试函数传入参数.sh 111 |
注意:
如果调用时不给函数的参数$1,函数也不会调用传入shell的$1,如下
1 | !/bin/bash |
再次调用sh程序,$1没有内容
1 | root@lthero:Test[766]$ bash 测试函数传入参数.sh 111 |
循环
for…do…done(固定循环)
增强for循环
#像python的语法
for each_animal in cat dog elephant
do
echo $each_animal#输出会自动换行
done
for循环的对象要求用空格分开即可,
如,将用户输入的aaa bbb ccc ddd
一行内容输出
1 | !/bin/bash |
测试如下
1 | root@lthero:Test[777]$ bash third.sh |
指定循环次数
类c语法:for(初始值;限制条件;赋值)
1 | for ((i=1; i<=100; i ++)) |
使用in
1 | for i in {1..100} |
使用seq
1 | for i in $(seq 1 100) |
注意:
- 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
过滤
1 | !/bin/bash |
为了将自己的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:
(“:
”也要)
运行测试
1 | root@lthero:Test[762]$ cknet |
综合实验二
输入进程名,判断是否在运行,返回进程pid
1 | !/bin/bash |
运行结果
1 | root@lthero:my_programs[641]$ ckrun ndoe |