shell脚本进阶
Posted 绮梦小煊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell脚本进阶相关的知识,希望对你有一定的参考价值。
一、逻辑运算
变量:
本地变量、环境变量、局部变量、位置参数变量、特殊变量
变量赋值:name=value,export name=value,declare -x name=value
变量引用:$name,${name}
注意:有些时候{}不能省略,例如
echo "$valuemagedu.com"
echo "${value}magedu.com"
撤销:unset name
bash脚本编程,运行脚本
#!/bin/bash #称为shebang是bash脚本的默认开头行固定格式
# # #号后面是注释信息,不显示也不影响脚本执行结果
空白行 忽略不显示行
bash的配置文件
porfile类:登录式shell
bashrc类:非登录式shell
登录式shell:/etc/profile–>/etc/profile.d/*.sh–>~/.bash_profile–>~/.bashrc–>/etc/bashrc
非登录式shell:~/.bashrc–>/etc/bashrc–>/etc/profile.d/*.sh
算数运算
+,-,*,/,**(乘次方),%(求模取余)
在bash中默认数据类型都是字符串形式,做算数运行需要经特殊方法才可以
示例:
算术运算格式:
(1)let VAR=算数运算表达式
#默认不输出结果到屏幕,所以需要赋值给变量,再引用
let 参数 [参数 …]
算术运算格式含义
num++,num– (两个减号不是大横线)变量后递增,后递减
++num,–num(两个减号不是大横线)变量前递增,前递减
+,-加法、 减法
!,~ 逻辑和按位求反
** 求幂运算(乘次方)
*,/,% 乘法,除法,求模(求余)
<<,>> 左和右移位
<=,>=,<,> 比较
==、 != 平等,不平等
& 按位与
^ 按位异或
| 按位或
&& 逻辑与
|| 逻辑或
expr ?expr: expr
条件运算符
=, *=, /=, %=,+=, -=, <<=, >>=,&=,^=,|= 赋值
变量也可以作为let 命令的参数
(2)VAR=$[算数运算表达式]
但是不能直接运行,赋不赋值给变量都可以,需要命令直接引用,如echo
(3)VAR=$((算数运算表达式))
但是不能直接运行,赋不赋值给变量都可以,需要命令直接引用,如echo
(4)expr EXPRESSION 或 VAR=$(expr EXPRESSION)
expr OPTION
示例:
expr 参数的算术运算格式
ARG1 | ARG2
ARG1 是否为 null,也不是 0,否则为 ARG2
ARG1 & ARG2
ARG1 如果参数都不是 null 或 0,否则为 0
ARG1 < ARG2
ARG1 小于 ARG2
ARG1 <= ARG2
ARG1 小于或等于 ARG2
ARG1 = ARG2
ARG1 等于 ARG2
ARG1 != ARG2
ARG1 ARG2 不等于
ARG1 >= ARG2
ARG1 是大于或等于 ARG2
ARG1 > ARG2
ARG1 大于 ARG2
ARG1 + ARG2
ARG1 和 ARG2 的算术总和
ARG1 -ARG2
ARG1 减 ARG2 的算术差
ARG1 * ARG2
ARG1 和 ARG2 的算术积
ARG1 / ARG2
ARG1 除以 ARG2 的算术商
ARG1 % ARG2
ARG1 除以 ARG2 的算术余数
注意:算数运算格式内各参数彼此之间必须有空格,并且shell脚本不支持浮点型
增强型赋值:
变量做某种算数运算后回存至此变量中;
eg:
let i=$i+#
let i+=#
还有:+=,-=,*=,/=,%=
自增:
VAR=$[$VAR+1]
let VAR+=1
let VAR++
自减:
VAR=$[$VAR-1]
let VAR-=1
let VAR–
二、条件测试:
判断某需求是否满足,需要由测试机制来实现;
如何编写测试表达式以实现所需的测试;
(1)执行命令,并利用命令状态返回值来判断
0:成功
1-255:失败
(2)测试表达式
test EXPRESSION eg:test 3 -gt 2
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION两端必须有空白字符,否则有语法错误
bash的测试类型:
数值测试:数值比较
-eq:是否等于; eg:[ $num1 -eq $sum2 ]
-ne:是否不等于
-gt:是否大于
-ge:是否大于等于
-lt:是否小于
-le:是否小于等于
字符串测试:
==:是否等于;(字符串)
一个=号也可以由于赋值变量用=号,但是赋值并不应用到[]中,所以能够使用,但是还是推荐使用双=号
>:是否大于
<:是否小于
!=:是否不等于
=~:左侧字符串时候能够被右侧的PATTERN所匹配;(模糊匹配而非精确匹配)
示例:
-z "STRING":判断指定的字符串是否为空,空为真,不空为假
-n "STRING":判断指定的字符串是否为不空,不空为真,空为假
注意:(1)字符串测试时:字符串要加引号,表示引用有变量命令"",没有变量命令使用‘‘
(2)作比较时,尽量使用[[]];
(3) 但是在组合测试时,[[]]有时会有语法错误,所以可以使用[]
示例:
文件测试:
存在性测试:
-a FILE 少使用
-e FILE 推荐使用
文件存在则为真,否则为假
存在性及类型测试:
-b FILE:是否存在并且为块设备文件;
-c FILE:是否存在并且为字符设备文件;
-d FILE:是否存在并且为目录文件;
-f FILE:是否存在并且为普通文件;
-h FILE 或 -L FILE:是否存在并且为符号链接文件;
-p FILE:是否存在并且为管道文件;
-S FILE:是否存在并且为套接字文件;
文件权限测试:
-r FILE:是否存在并且对当前用户可读;
-w FILE:是否存在并且对当前用户可写;
-x FILE:是否存在并且对当前用户可执行;
特殊权限测试:
-u FILE:是否存在并且拥有suid权限;
-g FILE:是否存在并且拥有sgid权限;
-k FILE:是否存在并且拥有sticky权限;
注意:对于脚本则是当前运行此脚本的用户
文件是否有内容:
-s FILE:是否有内容;
时间戳测试:
-N FILE:文件自从上一次读取操作后是否被修改过
从属关系测试:
-O FILE:当前用户是否为文件的属主;
-G FILE:当前用户是否属于文件的属组;
双目测试:
FILE 1 -ef FILE2 :FILE1与FILE2是否指向同一个文件系统的相同inode的硬链接;
FILE 1 -nt FILE2 :FILE1的最近一次更新时间是否新于FILE2的最近一次更新时间
FILE 1 -ot FILE2 :FILE1的最近一次更新时间是否旧于FILE2的最近一次更新时间
组合测试条件:
逻辑运算:
第一种方式:
COMMAND1 && COMMAND2
COMMAND1 || COMMAND2
! COMMAND
eg: [ -O FILE ] && [ -r FILE ]
第二种方式:
test EXPRESSION1 -a EXPRESSION2
[ EXPRESSION1 -a EXPRESSION2 ]
[[ EXPRESSION1 -a EXPRESSION2 ]]
类此还有
EXPRESSION1 -o EXPRESSION2
! EXPRESSION
eg:[ -O FILE -a -r FILE ]
注意:模式匹配时,势必要激活正则表达式引擎,而使用正则表达式的测试判断,要比单一字符串测试判断要慢,所以尽量避免使用正则表达式进行查找比较和测试;
脚本的状态返回值:
默认是脚本中执行的最后一条件命令的状态返回值
自定义状态退出状态码:exit [n]:n为自己指定的状态码;
注意:shell进程遇到exit时,即会终止,因此,整个脚本执行即为结束;
三、bash脚本语句
过程式编程语言的代码执行顺序:
顺序执行:从左至右,从上至下,逐一运行
选择执行:代码有一个分支:条件满足时才会执行
两个或以上的分支:只会执行其中一个满足条件的分支
循环执行:
代码片段(循环体)要执行0、1或多次重复运行
选择执行:
单分支的if语句
if 测试条件;then
代码分支
条件为真则执行,为假则不执行
或者
fi
if 测试条件
then
代码分支
条件为真则执行,为假则不执行 fi
双分支的if语句:
if 测试条件;then
条件为真时执行的分支
else
条件为假时执行的分支
fi
多分支的if语句:
if COMDITION1;then
条件1为真分支
elif COMDITION2;then
条件2为真分支
elif COMDITION3;then
条件3为真分支
…
elif COMDITIONn;then
条件n为真分支
else
所有条件均不满足时的分支
fi
注意:即便多个条件可能同时都能满足,分支只会执行其中一个,首先测试为“真”;并且if语句可嵌套if等其他语句
case语句的语法格式:
case $VARAIBLE in
PATH1)
分支
;;
PATH2)
分支
;;
…
*)
分支
;;
esac
注意:双分号如果不加,则会前一个匹配执行后,下一个会接着匹配执行
适用于一个变量,与多个可能取值比较
支持glob风格的通配符:
*:任意长度的任意字符;
?:任意单个字符
[]:范围内任意个字符
a|b:a或b;
bash脚本编程之用户交互:
脚本参数
用户交互:通过键盘输入数据,从而完成变量赋值操作
read [option]…[name…]
-p ‘PROMPT‘ 提示注释信息
-t TIMEOUT 时间限制
bash -n /path/to/some_script
检测脚本中的语法错误
bash -x /path/to/some_script
调试执行
循环执行:将一段代码重复执行0、1或多次;
进入条件:条件满足时才进入循环
退出条件:每一个循环都应该有退出条件,以有机会退出循环
bash脚本:
for循环
while循环
until循环
for循环:
执行机制:依次将列表中的元素赋值给“变量名” ; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
两种格式:
(1)遍历列表
(2)控制变量
遍历列表
for VARAIBLE in LIST;do
循环体
done
进入条件:LIST列表有元素,即可进入循环
退出条件:列表中的元素遍历完成
LIST的生成方式:
(1)直接给出;
(2)整数列表
(a){start…end}
(b)seq [start [incremtal]] last
(3)返回列表的命令:$(COMMAND),例如:ls cat
(4)glob通配符
(5)变量引用;例如[email protected],$*
格式示例:
#!/bin/bash
#
for username in user1 user2 user3;do
if id $username &>/dev/null;then
echo "$username exists."
else
useradd $username && echo "Add user $username finished."
fi
done
示例:求100以内的正整数之和
#!/bin/bash
#
delcare -i sum=0
for i in {1..100};do
echo "\$sum is $sum,\$i is $i"
sum=$[$sum+$i]
done
echo $sum
示例:判断/var/logm目录下的每一个文件的内容类型
#!/bin/bash
#
for filename in /var/log/*;do
if [ -f $filename ];then
echo "Common file."
elif [ -L $filename ];then
echo "Sysbolic link"
elif [ -d $filename ];then
echo "Directory."
elif [ -b $filename ];then
echo "block special file."
elif [ -c $filename ];then
echo "character special file."
elif [ -S $filename ];then
echo "Socket file."
else
echo "Unkown."
fi
done
while循环:
while CONDITION;do
循环体
循环控制变量修正表达式
done
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正
进入条件:CONDITION测试为“真”
退出条件:CONDITION测试为“假”
示例:求100以内的正整数之和
#!/bin/bash
#
declare -i sum=0
declare -i i=1
while [ $i -le 100 ];do
sum=$[$sum+$i]
let i++
done
echo $sum
1、编写脚本/root/bin/createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
在,就显示其存在,否则添加之;显示添加的用户的 id 号等信息 |
[[ -z $1 ]] && echo "请输入一段字符串作为用户名" && exit |
if grep -E "^$1:" /etc/ passwd &>/dev/null; then |
2、编写脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息
[[ -z $1 ]] && echo "请输入一段字符串" && exit |
var=` echo "$1" | tr "[[:lower:]]" "[[:upper:]]" ` |
if [[ ${var} =~ "YES" ]]; then |
elif [[ ${var} =~ "NO" ]]; then |
3、编写脚本/root/bin/filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
echo -e "Error: No argument.\n\tUsage: $0 FILENAME" |
echo "$1: No such file or diretory" |
echo "$1 is a common file" |
4、编写脚本/root/bin/checkint.sh,判断用户输入的参数是否为正整数
[ -z "$num" ]&& echo "请输入一个数字" && exit 1 |
NUM=$( echo $num | grep -Eo "\-?[[:digit:]]+" ) |
if [ "$NUM" == "$num" ]; then |
if [ "$NUM" - lt "0" ]; then |
elif [ "$NUM" - eq "0" ]; then |
echo "您输入的不是一个整数,请重新运行脚本!" |
练习:用for实现
1、判断/var/目录下所有文件的类型
FileType=` ls -dl $DirName/$i | cut -c1` |
echo "$i is a common file." |
echo "$i is a link file." |
echo "$i is a block file." |
echo "$i is a character file." |
echo "$i is a pipe file." |
echo "$i is a socket file." |
echo "$1 is not a diretory" |
2、 添加10个用户user1-user10,密码同用户名
echo -e "Error: No option \n\t-d\tdelete user1-user10\n\t-a\tadd user1-user10 " |
if id user$i &> /dev/null; then |
echo "user$i: Delete complete!" |
echo "user$i: No such user!" |
if id user$i &> /dev/null; then |
echo "user$i" | passwd --stdin "user$i" &> /dev/null |
echo -e "user$i: Already existed!\nAnd authentication tokens updated successful!" |
useradd user$i &> /dev/null |
echo "user$i" | passwd --stdin "user$i" &> /dev/null |
echo " user$i: Add complete!" |
echo -e "$0 : Unknow option!\nplease use ‘-a‘‘--add‘ to add user or ‘-d‘‘--dell‘to delect user" |
3、 /etc/rc.d/rc3.d目录下分别有多个以K开头和以S开头的文件;分别读取每个文件,以K开头的文件输出为文件加stop,以S开头的文件输出为文件名加start;
“ K34filename stop”
“S66filename start”
for i in ` ls /etc/rc.d/rc3.d` |
FileC1=` echo $i | cut -c1` |
4、编写脚本,提示输入正整数n的值,计算1+2+3+…n的总和
echo "\$sum is $sum,\$i is $i" |
5、编写脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态
Segment=` echo $IP | cut -d. -f1-3 `. |
if echo $IP | egrep ‘\<([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>‘ &>/dev/null |
if ping -c 1 -W 1 $Segment$i &> /dev/null |
echo -e "$Segment$i\tonline" |
echo -e "$Segment$i\toffline" |
6、 打印九九乘法表
echo -n -e "$j*$i=$[$j*$i]\t" |
用while实现
1、 编写脚本,求100以内所有正整数之和
2、 编写脚本,通过ping命令探测172.16.250.1-254范围内的所有主机的在线状态,统计在线主机和离线主机各多少。
ping -c1 -W1 $a$i &> /dev/null |
echo "the sum number of active users is $j" |
echo "the sum number of inactive users is $k" |
3、 编写脚本,打印九九乘法表
4、 编写脚本,利用变量RANDOM生成10个随机数字,输出这个10数字,并显示其中的最大者和最小者
[ $max - lt $a ] && max=$a |
[ $min - gt $a ] && min=$a |
5、编写脚本,实现打印国际象棋棋盘
[ $z - eq 0 ] && echo - ne "\033[41;1m \033[0m" || echo - ne "\033[43;1m \033[0m" |
if [ $[$i%2] - eq 0 ] ; then |
if [ $[$j%2] - ne 0 ]; then |
echo -en "\e[43;37m \e[0m" |
echo -en "\e[45;37m \e[0m" |
if [ $[$j%2] - eq 0 ]; then |
echo -en "\e[43;37m \e[0m" |
echo -en "\e[45;37m \e[0m" |
1、每隔3秒钟到系统上获取已经登录的用户的信息;如果发现用户hacker登录,则将登录时间和主机记录于日志/var/log/login.log中,并提示该用户退出系统。
until who | grep -q "^hacker\b" ; do |
who | grep "^hacker" | tr -s ‘ ‘ | cut -d ‘ ‘ -f3,5 >> /var/log/login.log |
echo "you should logout system" | mail hacker |
echo "reminded and login record in /var/log/login.log" |
2、随机生成10以内的数字,实现猜字游戏,提示比较大或小,相等则退出。
until [[ $suiji - eq $num ]]; do |
[ $shuru - lt $suiji ] && echo "小了,往大了猜" |
[ $shuru - gt $suiji ] && echo "大了,往小了猜" |
1、写个脚本:
1 4
1,3空格1个*
2,2空格2个*
3,1空格,3*
n行空格数=总行数-n
n行*数=2n-1
maxLineNum=` seq 1 2$termCols | wc -l` |
echo -e "#当终端的宽度最多能够正常打印$maxLineNum行\n#请输入要打印的三角形行数" |
read -p "#行数范围大于1小于等于$maxLineNum,请输入行数:" lineNeeds |
[[ `tput cols` - ne $termCols ]] && echo "终端宽度已经被改变,请重新运行脚本" && exit 1 |
[[ $lineNeeds =~ ^[[:digit:]]+$ && $lineNeeds - ge 2 && $lineNeeds - le $maxLineNum ]] |
[[ $? - ne 0 ]] && echo "输入值不合法" && exit |
for line in ` seq 1 $lineNeeds`; do |
for ((kong=(lineNeeds-line);kong>0;kong--)); do |
for ((xing=1;xing<=(line*2-1);xing++)); do |
2、用until循环实现国际象棋棋盘
[ $z - eq 0 ] && echo - ne "\033[41;1m \033[0m" || echo - ne "\033[43;1m \033[0m" |
以上是关于shell脚本进阶的主要内容,如果未能解决你的问题,请参考以下文章
SHELL脚本编程进阶
Shell脚本之进阶
代码片段:Shell脚本实现重复执行和多进程
shell脚本进阶
shell 脚本 片段
Linux shell脚本进阶使用