为什么要学习shell脚本
用shell脚本可以做以下这些事情:
- 自动化管理:很多任务和需求可以实现自动化
- 跟踪与管理系统:事实上很多系统服务都是通过shell脚本启动的
- 简单入侵检测:定时扫描系统日志,就可以检测入侵行为
- 数据处理
- 跨平台的处理
第一个脚本的编写
shell脚本其实就是纯文本文件,有着如下这些规则
- 命令从上而下,从左到右执行
- 命令、参数、选项之间的多个空格会被忽略
- 空白行和tab会被忽略
- 如果读取到Enter,就会开始执行命令
- 多行命令可以用\来进行连接
- #可以用作注释
接下来我们来编写一个hello world:
python
[root@node4 bin]# vim hello.sh
python
#!/bin/bash
# show hello world to you
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0
接下来简单说明下每句话的含义
- 第一行声明了这个脚本使用的shell名称
- 其余的#内容是注释,一般会说明这个脚本的作用,脚本作者等
- 主要环境变量的声明,一般比较重要的是PATH和LANG
- 主程序部分,本脚本来说就是echo那一行
- 执行结果,如果正常执行返回0
执行脚本,可以通过以下命令执行
python
sh hello.sh
如果想通过./来执行,需要先添加权限
python
[root@node4 bin]# ./hello.sh
-bash: ./hello.sh: 权限不够
[root@node4 bin]# chmod a+x hello.sh
[root@node4 bin]# ./hello.sh
Hello World!
简单shell脚本练习
通过一些练习逐渐掌握shell脚本编写
1、交互式脚本
python
[root@node4 bin]# vim showname.sh
#!/bin/bash
# 用户输入姓名,程序打印完整姓名
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入你的姓:" lastname # 提示输入
read -p "请输入你的名:" firstname
echo -e "\n你的完整名字是: ${lastname} ${firstname}" # 打印输出
运行结果
python
[root@node4 bin]# sh showname.sh
请输入你的姓:koizumi
请输入你的名:hanayo
你的完整名字是: koizumi hanayo
2、利用date建立文件
python
[root@node4 bin]# vim create_file_by_date.sh
#!/bin/bash
# 根据日期生成文件
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "我将使用touch命令来生成3个文件" # 打印提示信息
read -p "请输入文件名称" fileuser # 提示用户输入
filename=${fileuser:-"filename"} # 如果无输入,使用默认值
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date +%Y%m%d)
file1=${filename}_${date1}
file2=${filename}_${date2}
touch "${file1}"
touch "${file2}"
运行结果如下:
python
[root@node4 bin]# sh create_file_by_date.sh
我将使用touch命令来生成3个文件
请输入文件名称myfile
[root@node4 bin]# ls
create_file_by_date.sh hello.sh myfile_20240416 myfile_20240418 showname.sh
3、简单数值运算
bash shell里仅支持整数的运算
python
#!/bin/bash
# 简单数学运算
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "请输入2个整数"
read -p "第一个整数:" numa
read -p "第二个整数:" numb
total=$((${numa}+${numb}))
echo -e "\n${numa}+${numb}的计算结果是==>${total}"
运行结果
python
[root@node4 bin]# sh math_operation.sh
请输入2个整数
第一个整数:1
第二个整数:1
1+1的计算结果是==>2
脚本执行类别的差异
脚本可以用source、sh和./来执行,那么这三种执行方式的差异是什么呢?
- 当使用sh的方式执行时,系统会开启一个bash的子进程来执行程序
- 利用source来执行是,则在父进程中执行
以上面的showname脚本来举例:
python
[root@node4 bin]# echo ${firstname} ${lastname}
# 可以看到执行前,这两个变量不存在
[root@node4 bin]# sh showname.sh
请输入你的姓:hanayo
请输入你的名:kayotin
你的完整名字是: hanayo kayotin
[root@node4 bin]# echo ${firstname} ${lastname}
#在执行后,这两个变量还是不存在
用source执行:
python
[root@node4 bin]# source showname.sh
请输入你的姓:watanabe
请输入你的名:yo
你的完整名字是: watanabe yo
[root@node4 bin]# echo ${firstname} ${lastname}
yo watanabe
# 可以看到变量是存在的
判断式的使用
之间我们简单介绍过使用&&或者||基于前一个命令的执行结果来执行下一条命令,那时候我们是用的$?这个变量来判断的。除此之外还有其他其中判断方法
使用test命令
检测某个目录是否存在
python
[root@node4 ~]# test -e /kayotin
[root@node4 ~]# test -e /kayotin && echo "目录存在" || echo "目录不存在"
目录不存在
[root@node4 ~]# test -e /root && echo "目录存在" || echo "目录不存在"
目录存在
其他一些常用参数:
-
文件类型的判断:
python-e 路径是否存在 -f 路径是否是文件 -d 路径是否是目录
-
文件权限检测
python-r 是否具有读权限 -w 写权限 -x 执行权限
-
两个文件之间的比较
python-nt 文件1是否比文件2新 -ot older than -ef 是否为同一个文件,判断hardlink
-
两个整数之间的比较
python-eq 相等 -ne 不等 -gt 大于 -lt 小于 -ge 大于等于 -le 小于等于
-
字符串判断
pythontest -z string 判断字符串是否为空,如果是空字符串,则为true test -n string 判断是否为非0 test str1 == str2 判断是否相等 test str1 != str2 判断是否不相等
-
多重条件判定
python-a 两个条件是否同时成立 -o or
使用test的例子
我们使用test写一个脚本,实现如下功能:
- 让用户输入一个路径,判断路径是否存在,不存在则输出不存在,然后退出
- 如果存在,判断是文件还是目录,输出判断结果
- 判断文件权限,然后输出结果
python
[root@node4 bin]# vim check_file.sh
#!/bin/bash
# 用test命令判断文件
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入完整路径:" file_path
# 判断字符串是否为空
test -z ${file_path} && echo "路径不能为空!" && exit 0
# 判断路径是否存在
test ! -e ${file_path} && echo "路径不存在" && exit 0
# 判断是目录还是文件
test -f ${file_path} && file_type="一般文件"
test -d ${file_path} && file_type="目录"
test -r ${file_path} && file_perm="读"
test -w ${file_path} && file_perm="${file_perm}写"
test -x ${file_path} && file_perm="${file_perm}执行"
# 输出
echo "路径:${file_path}是一个${file_type}"
echo "权限是${file_perm}"
执行效果如下:
python
[root@node4 bin]# sh check_file.sh
请输入完整文件路径:/root/myfile
路径:/root/myfile是一个一般文件
权限是读写
使用[]命令进行判断
使用中括号命令进行判断也是一种常用方式,
python
[root@node4 bin]# vim y_n.sh
#!/bin/bash
# 判断用户输入
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入y或者n:" yn
[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "你输入了y" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "你输入了n" && exit 0
echo "非法输入" && exit 0
注意,使用中括号的判断,前后一定要有空格,也就是[空格 命令。。。空格]
shell脚本中的默认变量$0...
系统命令都有很多的参数,例如ls -la,shell脚本也是如此,
python
/文件/具体/路径.sh opt1 opt2 opt3 opt 4
$0 $1 $2 $3 $4
我们用一个例子来理解一下:
python
[root@node4 bin]# vim paras.sh
#!/bin/bash
# 展示默认参数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "本shell脚本的名字是 ==> ${0}"
echo "参数的总个数是 ==> $#"
[ "$#" -lt 2 ] && echo "参数总数小于两个" && exit 0
echo "全部参数是 ==> '$@'"
echo "第一个参数是 == > ${1}"
echo "第二个参数是 == > ${2}"
运行结果如下:
python
[root@node4 bin]# sh paras.sh a b c
本shell脚本的名字是 ==> paras.sh
参数的总个数是 ==> 3
全部参数是 ==> 'a b c'
第一个参数是 == > a
第二个参数是 == > b
用shift命令可以对参数进行偏移
条件判断语句
所有编程语言里面都有条件判断,就是if之类的,当然shell也是如此
if...then
if...then是shell中最常见的判断条件,基本语法如下
python
if [ 条件判断语句 ] ; then
条件成立时,需要执行的语句
fi <==表示结束if
我们将之前那个输入y或n的脚步改写成if...then的样子
python
[root@node4 bin]# vim y_n_if.sh
#!/bin/bash
# if then的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入y或者n:" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
echo "你输入了y"
exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
echo "你输入了n"
exit 0
fi
echo "非法输入!"
运行如下
python
[root@node4 bin]# sh y_n_if.sh
请输入y或者n:y
你输入了y
[root@node4 bin]# sh y_n_if.sh
请输入y或者n:a
非法输入!
if...then...else
shell语言也有else语句和else if语句来应对复杂的条件判断,基本语法如下
python
if [ 判断条件1 ]; then
条件1成立时,执行的语句
elif [ 判断条件2 ]; then
条件2成立时,执行的语句
else
以上条件都不成立时执行
fi
我们使用if...else改写一下上一个程序
python
[root@node4 bin]# vim y_n_else.sh
#!/bin/bash
# if else的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入y或者n:" yn
if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
echo "你输入了y"
exit 0
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
echo "你输入了n"
exit 0
else
echo "非法输入!"
fi
用if...else结合之前的参数功能
python
[root@node4 bin]# vim hello_para.sh
#!/bin/bash
# if else结合参数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
if [ "${1}" == "hello" ]; then
echo "how are you--"
elif [ "${1}" == "" ]; then
echo "请输入参数,例如 ==> {${0} para}"
else
echo "仅支持hello参数,例如 ==> {${0} hello}"
fi
再举一个例子,我们用netstat命令配合if...esle来检测端口开放情况,如果无法使用netstat命令,可以用yum install net-tools来安装:
python
[root@node4 bin]# vim netstat.sh
#!/bin/bash
# if else配合检测端口
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "即将进行端口检测..."
testfile=/root/bin/net_check.txt
netstat -tuln > ${testfile}
testing=$(grep ":80 " ${testfile})
if [ "${testing}" != "" ]; then
echo "80端口是开放的。"
fi
testing=$(grep ":22 " ${testfile})
if [ "${testing}" != "" ]; then
echo "22端口是开放的。"
fi
testing=$(grep ":25 " ${testfile})
if [ "${testing}" != "" ]; then
echo "25端口是开放的。"
fi
case...esac语句
case语句在python里没有,在其他语言也挺常见的,shell中的case语句基本语法如下:
python
case $变量名称 in
"变量1")
执行的内容
;;
"变量2")
执行的内容
;;
esac <== 表示结束case语句
用下面这个例子来直观感受一下
python
[root@node4 bin]# vim hello_case.sh
#!/bin/bash
# case语句使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case ${1} in
"hello")
echo "你好"
;;
"")
echo "请输入至少一个参数"
;;
*)
echo "仅能使用如下参数 ${0} {hello}"
;;
esac
函数function
shell中的函数基本语法如下:
python
function funcname() {
......
}
简单举例如下:
python
[root@node4 bin]# vim func123.sh
#!/bin/bash
# function函数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit() {
echo -n "你的选择是:"
}
echo "本程序将会打印你的输入"
case ${1} in
"one")
printit; echo ${1} | tr 'a-z' 'A-Z'
;;
"two")
printit; echo ${1} | tr 'a-z' 'A-Z'
;;
*)
echo "仅支持如下参数: ${0} {one|two}"
;;
esac
我们可以往函数中传递参数,将上面例子简单改写一下
python
[root@node4 bin]# vim func123-1.sh
#!/bin/bash
# functi:on函数的使用
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit() {
echo -n "你的选择是:${1} "
}
echo "本程序将会打印你的输入"
case ${1} in
"one")
printit 1
;;
"two")
printit 2
;;
*)
echo "仅支持如下参数: ${0} {one|two}"
;;
esac
循环语句
接下来我们学习一下shell中的循环语句
while do done 和 until do done
当你不确定循环要做多少次的时候,一般使用while循环,语法如下
python
while [循环条件]
do
执行语句
done
shell中还有一种until循环,意思是 当满足制定条件时,终止循环;否则一直执行。
python
until [循环终止条件]
do
执行语句
done
while循环的例子,当输入不是yes也不是YES时,就执行循环
python
[root@node4 bin]# vim while_yes.sh
#!/bin/bash
# while循环的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
while [ "${yn}" != "yes" -a "${yn}" != "YES" ]
do
read -p "请输入yes或YES来终止程序:" yn
done
echo "你输入了正确的选择"
until循环的例子,当输入时yes或者YES时,就终止循环
python
[root@node4 bin]# vim until_yes.sh
#!/bin/bash
# until循环的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
until [ "${yn}" = "yes" -o "${yn}" = "YES" ]
do
read -p "请输入yes或YES来终止程序:" yn
done
echo "你输入了正确的选择"
for...do...done循环
已知要进行几次循环的情况下,一般使用for循环,基本语法如下
python
for var in con1 con2 con3 ...
do
执行程序
done
基本for循环例子:
python
#!/bin/bash
# for循环的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
for animal in dog cat neko
do
echo "当前动物是:${animal}"
done
打印本机所有用户
python
#!/bin/bash
# for循环打印用户
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd)
for user in ${users}
do
echo "${user}"
done
检测本地哪些ip已占用
python
#!/bin/bash
# for循环的例子3
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
network="192.168.32"
for ip in $(seq 1 20)
do
ping -c 1 -w 1 ${network}.${ip} &> /dev/null && result=0 || result=1
if [ "${result}" == 0 ]; then
echo "IP ${network}.${ip} 在使用中"
else
echo "IP ${network}.${ip} 未占用"
fi
done
for...done的另一种写法
其实就类似其他语言的 for(i=0, i<10,i++)
python
for (( 初始值; 限制值; 赋值运算))
do
程序
done
我们计算一个累加,
python
#!/bin/bash
# for循环的例子4
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入一个整数,我将计算1+2+..到n的值:" num
s=0
for (( i=1; i<=${num}; i=i+1 ))
do
s=$((${s}+${i}))
done
echo "1+2+...+${num}的计算结果是:${s}"
随机数RANDOM
很多时候我们需要产生一个随机数,这时可以使用random
python
#!/bin/bash
# 随机数的例子
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
eat[1]="麦当劳"
eat[2]="KFC"
eat[3]="泡面"
eat[4]="螺蛳粉"
eatnum=4
check=$(( RANDOM % ${eatnum} + 1 ))
echo "今天吃${eat[${check}]}"
Shell脚本的调试
执行shell脚本是时,如果有语法错误,除了在执行时报错之外,也可以提前进行调试。
python
sh [-nvx] scripts.sh
参数如下:
-n 不执行脚本,仅查询语法问题
-v 执行脚本前,先输出内容
-x 输出执行脚本内容
比如:
python
[root@node4 bin]# sh -x for_1.sh
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/bin
+ export PATH
+ for animal in dog cat neko
+ echo 当前动物是:dog
当前动物是:dog
+ for animal in dog cat neko
+ echo 当前动物是:cat
当前动物是:cat
+ for animal in dog cat neko
+ echo 当前动物是:neko
当前动物是:neko
小结
本章我们学习了shell脚本的基本语法,包括
用户交互、脚本的编写和执行、条件判断语句if和case、循环语句while和for以及使用sh -x 进行shell脚本的简单debug。