在linux中,Bash脚本是很基础的知识,虽然复杂的脚本是很烧脑,但是,当我们熟练的掌握了其中的用法与技巧,再多加练习,总有一天也会成为得心应手的脚本大神。脚本在生产中的作用,可以省下很多复杂的操作,减轻自己的工作压力。
条件选择、判断 条件选择if (1)用法格式
1 2 3 4 5 6 7 8 9 if 判断条件 1 ; then elif 判断条件 2 ; then elif 判断条件 3 ; then else fi
逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if。
(2)经典案例:
判断年纪
1 2 3 4 5 6 7 8 9 10 11 12 13 #!/bin/bash read -p "Please input your age: " ageif [[ $age =~ [^0-9] ]] ;then echo "please input a int" exit 10 elif [ $age -ge 150 ];then echo "your age is wrong" exit 20 elif [ $age -gt 18 ];then echo "good good work,day day up" else echo "good good study,day day up" fi
分析:请输入年纪,先判断输入的是否含有除数字以外的字符,有,就报错;没有,继续判断是否小于150,是否大于18。
判断分数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/bin/bash read -p "Please input your score: " scoreif [[ $score =~ [^0-9] ]] ;then echo "please input a int" exit 10 elif [ $score -gt 100 ];then echo "Your score is wrong" exit 20 elif [ $score -ge 85 ];then echo "Your score is very good" elif [ $score -ge 60 ];then echo "Your score is soso" else echo "You are loser" fi
分析:请输入成绩,先判断输入的是否含有除数字以外的字符,有,就报错;没有,继续判断是否大于100,是否大于85,是否大于60。
条件判断 case (1)用法格式
1 2 3 4 5 6 7 8 9 10 11 case $name in ;PART1) cmd ;; PART2) cmd ;; *) cmd ;; esac
1 2 3 4 5 注意:case 支持glob 风格的通配符: *: 任意长度任意字符 ?: 任意单个字符 [] :指定范围内的任意单个字符 a|b: a 或b
(2)案例:
判断yes or no
1 2 3 4 5 6 7 8 9 10 11 12 13 #!/bin/bash read -p "Please input yes or no: " anwcase $anw in [yY][eE][sS]|[yY]) echo yes ;; [nN][oO]|[nN]) echo no ;; *) echo false ;; esac
分析:请输入yes or no,回答Y/y、yes各种大小写组合为yes;回答N/n、No各种大小写组合为no。
循环 for (1)用法格式
1 2 3 4 for name in 列表 ;do done
1 2 3 4 for (( exp1; exp2; exp3 )) ;do done
exp1只执行一次,相当于在for里嵌了while
执行机制:依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
列表的表示方法,可以glob 通配符,如{1..10} 、*.sh ;也可以变量引用,如: seq 1 $name
(2)案例
求出(1+2+…+n)的总和
1 2 3 4 5 6 7 8 9 10 11 12 13 sum =0read -p "Please input a positive integer: " numif [[ $num =~ [^0-9] ]] ;then echo "input error" elif [[ $num -eq 0 ]] ;then echo "input error" else for i in `seq 1 $num ` ;do sum =$[$sum +$i ] done echo $sum fi unset zhi
分析:sum初始值为0,请输入一个数,先判断输入的是否含有除数字以外的字符,有,就报错;没有判断是否为0,不为0进入for循环,i的范围为1~输入的数,每次的循环为sum=sum+i,循环结束,最后输出sum的值。
求出(1+2+…+100)的总和
1 2 3 4 for (( i=1 ,num=0 ;i<=100 ;i++ ));do [ $[i%2] -eq 1 ] && let sum +=i done echo sum =$sum
分析:i=1,num=0;当i<=100,进入循环,若i÷2取余=1,则sum=sum+i,i=i+1。
while (1)用法格式
循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true” ,则执行一次循环;直到条件测试状态为“false” 终止循环
(2)特殊用法(遍历文件的每一行):
1 2 3 while read line; do 控制变量初始化 循环体 done < /PATH/FROM/SOMEFILE
1 2 3 cat /PATH/FROM/SOMEFILE | while read line; do 循环体 done
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
(3)案例:
100以内所有正奇数之和
1 2 3 4 5 6 7 8 9 10 11 sum =0i=1 while [ $i -le 100 ] ;do if [ $[$i %2] -ne 0 ];then let sum +=i let i++ else let i++ fi done echo "sum is $sum "
分析:sum初始值为0,i的初始值为1;请输入一个数,先判断输入的是否含有除数字以外的字符,有,就报错;没有当i<100时,进入循环,判断 i÷2取余 是否不为0,不为0时为奇数,sum=sum+i,i+1,为0,i+1;循环结束,最后输出sum的值。
until 循环 (1)用法
进入循环:循环条件为false ;退出循环:循环条件为true;刚好和while相反,所以不常用,用while就行。
(2)案例
监控xiaoming用户,登录就杀死
1 2 3 4 until pgrep -u xiaoming &> /dev/null ;do sleep 0.5 done pkill -9 -u xiaoming
分析:每隔0.5秒扫描,直到发现xiaoming用户登录,杀死这个进程,退出脚本,用于监控用户登录。
4、select 循环与菜单
(1)用法
1 2 3 4 select variable in listdo 循环体命令 done
select 循环主要用于创建菜单,按数字顺序排列的示菜单项将显示在标准错误上,并显示PS3 提示符,等待用户输入
用户输入菜单列表中的某个数字,执行相应的命令
用户输入被保存在内置变量 REPLY 中
select 是个无限循环,因此要记住用 break 命令退出循环,或用 exit 按 命令终止脚本。也可以按 ctrl+c退出循环
select 和 经常和 case 联合使用
与for循环类似,可以省略 in list, 此时使用位置参量
(2)案例:
生成菜单,并显示选中的价钱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 PS3="Please choose the menu: " select menu in mifan huimian jiaozi babaozhou quitdo case $REPLY in 1|4) echo "the price is 15" ;; 2|3) echo "the price is 20" ;; 5) break ;; *) echo "no the option" esac done
分析:PS3是select的提示符,自动生成菜单,选择5break退出循环。
循环里的一些用法 循环控制语句 (1)语法
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
break [N]:提前结束第N层循环,最内侧为第1层
1 2 3 4 5 6 7 while CONDTITON1; do CMD1 if CONDITION2; then continue / break fi CMD2 done
(2)案例:
求(1+3+…+49+53+…+100)的和
1 2 3 4 5 6 7 #!/bin/bash sum =0for i in {1..100} ;do [ $i -eq 51 ] && continue [ $[$i %2] -eq 1 ] && { let sum +=i;let i++; } done echo sum =$sum
分析:做1+2+…+100的循环,当i=51时,跳过这次循环,但是继续整个循环,结果为:sum=2449
求(1+3+…+49)的和
1 2 3 4 5 6 7 #!/bin/bash sum =0for i in {1..100} ;do [ $i -eq 51 ] && break [ $[$i %2] -eq 1 ] && { let sum +=i;let i++; } done echo sum =$sum
分析:做1+2+…+100的循环,当i=51时,跳出整个循环,结果为:sum=625
循环控制shift命令 (1)作用
用于将参数列表list左移指定次数,最左端的那个参数就从列表中删除,其后边的参数继续进入循环
(2)案例:
创建指定的多个用户
1 2 3 4 5 6 7 8 9 10 #!/binbash if [ $# -eq 0 ] ;then echo "Please input a arg(eg:`basename $0 ` arg1)" exit 1 else while [ -n "$1 " ];do useradd $1 &> /dev/null shift done fi
分析:如果没有输入参数(参数的总数为0),提示错误并退出;反之,进入循环;若第一个参数不为空字符,则创建以第一个参数为名的用户,并移除第一个参数,将紧跟的参数左移作为第一个参数,直到没有第一个参数,退出。
打印直角三角形的字符
1 2 3 4 5 6 #!/binbash while (( $# > 0 ))do echo "$*" shift done
返回值结果 true 永远返回成功结果
: null command ,什么也不干,返回成功结果
false 永远返回错误结果
创建无限循环
1 2 3 while true ;do 循环体 done
循环中可并行执行,使脚本运行更快 (1)用法
1 2 3 4 5 6 for name in 列表 ;do { 循环体 }& done wait
(2)实例:
搜寻自己指定ip(子网掩码为24的)的网段中,UP的ip地址
1 2 3 4 5 6 7 8 9 10 11 12 read -p "Please input network (eg:192.168.0.0): " netecho $net |egrep -o "\<(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>" [ $? -eq 0 ] || ( echo "input error" ;exit 10 ) IP=`echo $net |egrep -o "^([0-9]{1,3}\.){3}" ` for i in {1..254};do { ping -c 1 -w 1 $IP$i &> /dev/null && \ echo "$IP$i is up" }& done wait
分析:请输入一个IP地址例192.168.37.234,如果格式不是0.0.0.0 则报错退出;正确则进入循环,IP变量的值为192.168.37. i的范围为1-254,并行ping 192.168.37.1-154,ping通就输出此IP为UP。直到循环结束。
字符串 在我们熟知语言中都有自己的数据类型,那么shell是数据类型有哪些?shell属于弱类型语言,它不会强制你声明变量的数据类型。当给一个变量赋值时,根据赋值的内容自动确定数据类型 。虽然不能直接定义数据类型,但是我们可以通过一些方法来判断变量的值是否符合某种类型的预期。常见的类型有:字符串、数值、数组、关联数组(map)、日期、布尔、空值。本文主要介绍字符串。
declare 在介绍类型前,先回顾下declare命令. 它可以指定变量的属性 ,通过-p参数显示每个变量的属性和值 ,
格式如下:
1 declare [-aAfFgilrtux] [-p] [name[=value]]
这里仅简单介绍与本文相关的属性:
属性
说明
-a
表示当前变量数据类型为数组.
-A
表示当前变量数据类型为关联数组.
-i
表示当前变量数据类型为整数.
--
表示当前变量数据类型为字符串,默认
其他相关参数细节参见Shell变量定义与基本操作
定义 本节介绍3中定义字符串方式以及区别:双引号、单引号、无引号 。
双引号 1 2 3 [root@ ~]# s1="a" ; declare -p s1 declare -- s1="a"
单引号 1 2 [root@ ~]# s2='b' ; declare -p s2 declare -- s2="b"
需要注意,单引号中的字符不会被转义,保持原样输出,参见条件测试操作符 单引号和双引号区别
1 2 3 4 5 [root@ ~]# s11="a-$s1 " ; echo $s11 a-a [root@ ~]# s22='b-$s2' ; echo $s22 b-$s2
无引号 1 2 3 [root@ ~]# s3=c ; declare -p s3 declare -- s3="c"
基本操作 主要介绍字符串常见的操作:字符串长度、字符串截取、字符串匹配、字符串替换、字符串拼接、字符串去空格
字符串长度 格式:${#字符串} 或 ${#字符串[0]}
1 2 3 4 5 [root@ ~]# s1="abcdef" [root@ ~]# echo "s1_len:${#s1} " [root@ ~]# echo "s1_len:${#s1[0]} " s1_len:6 s1_len:6
字符串截取 格式:${字符串: offset:len}
offset: 截取下标位置,索引下标起始从0开始。注:offset前存在空格。
len: 截取多少个字符,省略则截取offset后所有字符串。
offset ≥ 0 从字符串起始 位置右 偏移量的位置开始向右截取 len字符,offset前空格可省略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@ ~]# s1="abcdef" ; s2=${s1:1:3} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:bcd [root@ ~]# s1="abcdef" ; s2=${s1:2} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:cdef [root@ ~]# s1="abcdef" ; s2=${s1: 3:2} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:de [root@ ~]# s1="abcdef" ; s2=${s1:3:2} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:de
offset < 0 从字符串末尾 开始左 偏移量的位置开始向右截取 len字符,表达式offset前空格必须存在,否则截取失败 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@ ~]# s1="abcdef" ; s2=${s1: -3:2} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:de [root@ ~]# s1="abcdef" ; s2=${s1: -4:2} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:cd [root@ ~]# s1="abcdef" ; s2=${s1: -4} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:cdef [root@ ~]# s1="abcdef" ; s2=${s1:-3:2} [root@ ~]# echo "s1:$s1 " ; echo "s2:$s2 " s1:abcdef s2:abcdef
字符串匹配 比较 使用比较操作符中的”等于 “匹配字符串,适合全匹配场景。
字符串其他的比较操作符参见基本运算符 中【字符串运算符】小节
1 2 3 4 5 6 7 8 9 10 11 12 13 s1="abc" s2="abc" s3="ABC" if [ "$s1 " = "$s2 " ]; then echo "yes" else echo "no" fi if [ "$s1 " = "$s3 " ]; then echo "yes" else echo "no" fi
通配符 使用通配符进行模式匹配,
常见的通配符有 * ? []
注意示例演示使用的 [[ ]] 运算符
通配符 * 匹配任意数量 的任何字符
1 2 3 4 5 6 7 8 9 10 11 12 13 s=abcdef if [[ $s == *"cdef" ]] ; then echo "yes" else echo "no" fi if [[ $s == *"cdef" * ]] ; then echo "yes" else echo "no" fi
通配符 ? 匹配单个字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 s=abcdef if [[ $s == ?"bcdef" ]] ; then echo "yes" else echo "no" fi if [[ $s == ?"bcdef" ? ]] ; then echo "yes" else echo "no" fi if [[ $s == ?"cdef" ]] ; then echo "yes" else echo "no" fi
通配符 [] 指定字符集,注意大小写
[abc] :匹配包含在方括号内的任意一个字符
1 2 3 4 5 6 s=abcdef if [[ $s == [abc]"bcdef" ]] ; then echo "yes" else echo "no" fi
[a-z] :匹配从a到z之间的任意一个字符
1 2 3 4 5 6 s=abcdef if [[ $s == [a-b]"bcdef" ]] ; then echo "yes" else echo "no" fi
[^abc] :匹配不包含在方括号内的任意字符
1 2 3 4 5 6 7 8 s=abcdef if [[ $s == [^b]"bcdef" ]] ; then echo "yes" else echo "no" fi
正则 在Shell脚本中,可以使用grep 命令进行正则表达式匹配字符串,
一些常见的正则表达式元字符:
元字符
说明
点号
.
星号
*
加号
+
问号
?
大括号
{}
方括号
[]
转义符
\
grep命令参数:
参数
说明
-q
使grep命令处于静默模式,不输出匹配结果,只根据返回值判断匹配是否成功。
-E
表示使用扩展正则表达式语法,加上-E参数正则生效,
如:
点号 . 匹配任意单个字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 s=abcdef if echo $s | grep -q "ab.def" ; then echo "yes" else echo "no" fi if echo $s | grep -q "ab.ef" ; then echo "yes" else echo "no" fi
星号 * 匹配前面的字符0次或多次 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 s="abbbc" if echo $s | grep -q "abbb*c" ; then echo "yes" else echo "no" fi if echo $s | grep -q "ab*c" ; then echo "yes" else echo "no" fi [root@ ~]# ls *.sh a.sh abc.sh 1.sh
加号 + 匹配前面的字符1次或多次 。
注grep命令不支持+, 使用-E参数扩展正则表达式语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 s="abbcccd" if echo $s | grep -qE "abbccc+d" ; then echo "yes" else echo "no" fi if echo $s | grep -qE "abbcc+d" ; then echo "yes" else echo "no" fi
问号 ? 匹配前面的字符0次或1次。
注grep命令不支持?, 使用-E参数扩展正则表达式语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 s="abbcccd" if echo $s | grep -qE "abbcccc?d" ; then echo "yes" else echo "no" fi if echo $s | grep -qE "abbccc?d" ; then echo "yes" else echo "no" fi [root@ ~]# ls ?.sh 1.sh s.sh t.sh x.sh
大括号 {} 匹配字符重复次数范围。 注grep命令不支持{}, 使用-E参数扩展正则表达式语法
1 2 3 4 5 6 7 8 9 10 11 12 13 s="abbcccd" if echo $s | grep -qE "abbc{1,2}" ; then echo "yes" else echo "no" fi if echo $s | grep -qE "abbc{1,2}d" ; then echo "yes" else echo "no" fi
方括号 [] 匹配指定字符集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 s="abcdef" if echo $s | grep -q "[b]cdef" ; then echo "yes" else echo "no" fi if echo $s | grep -qE "[a-z]{3}ef" ; then echo "yes" else echo "no" fi
转义字符 \ 匹配转义后的特殊字符。
注grep命令不支持\, 使用-E参数扩展正则表达式语法
1 2 3 4 5 6 7 8 9 10 11 12 s="ab?c" if echo $s | grep -qE "ab\?c" ; then echo "yes" else echo "no" fi if echo $s | grep -qE "ab?c" ; then echo "yes" else echo "no" fi
字符串替换 bash内置的字符串替换 1 2 3 4 [root@ ~]# s0="abcdef" old="bcd" new="BCD" [root@ ~]# s1=${s0/$old/$new} [root@ ~]# echo $1 aBCDef
sed命令替换 1 2 [root@ ~]# echo "abcdef" | sed 's/bcd/BCD/' aBCDef
awk命令替换 1 2 [root@ ~]# echo "abcdef" | awk '{gsub("bcd", "BCD"); print}' aBCDef
字符串拼接 简单拼接 1 2 3 [root@ ~]# s0="abc" s1="def" s2=$s0$s1 [root@ ~]# echo "$s2 " abcdef
双引号拼接 1 2 3 [root@ ~]# s0="abc" s1="def" s2="$s0$s1 " [root@ ~]# echo "$s2 " abcdef
字符串去空格 tr命令去除空格 1 2 3 4 5 [root@ ~]# s0=" ab cd ef gh " [root@ ~]# s1=$(echo "$s0 " | tr -d ' ' ) [root@ ~]# echo "[$s1 ]" [abcdefgh]
sed命令去除空格 1 2 3 4 5 [root@ ~]# s0=" ab cd ef gh " [root@ ~]# s1=$(echo "$s0 " | sed 's/ //g' ) [root@ ~]# echo "[$s1 ]" [abcdefgh]
awk命令去除空格 1 2 3 4 5 [root@ ~]# s0=" ab cd ef gh " [root@ ~]# s1=$(echo "$s0 " | awk '{gsub(" ", ""); print}' ) [root@ ~]# echo "[$s1 ]" [abcdefgh]
信号捕获trap 1、用法格式
trap ‘ 触发指令’ 信号,自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作
trap '' 信号,忽略信号的操作
trap '-' 信号,恢复原信号的操作
trap -p,列出自定义信号操作
信号可以3种表达方法:信号的数字2、全名SIGINT、缩写INT
2、常用信号
信号
含义
SIGHUP
无须关闭进程而让其重读配置文件
SIGINT
中止正在运行的进程;相当于Ctrl+c
SIGQUIT
相当于ctrl+\
SIGKILL
强制杀死正在运行的进程
SIGTERM
终止正在运行的进程(默认为15)
SIGCONT
继续运行
SIGSTOP
后台休眠
9
信号,强制杀死,捕获不住
3、案例
打印0-9,ctrl+c不能终止
1 2 3 4 5 6 #!/bin/bash trap 'echo press ctrl+c' 2for ((i=0 ;i<10 ;i++));do sleep 1 echo $i done
分析:i=0,当i<10,每休眠1秒,i+1,捕获2信号,并执行echo press ctrl+c
打印0-3,ctrl+c不能终止,3之后恢复,能终止
1 2 3 4 5 6 7 8 9 10 11 12 #!/bin/bash trap '' 2trap -pfor ((i=0 ;i<3 ;i++));do sleep 1 echo $i done trap '-' SIGINTfor ((i=3 ;i<10 ;i++));do sleep 1 echo $i done
分析:i=0,当i<3,每休眠1秒,i+1,捕获2信号;i>3时,解除捕获2信号
脚本小知识 1、生成随机字符
生成8个随机大小写字母或数字 cat /dev/urandom | tr -dc [:alnum:] | head -c 8
2、生成随机数
1 2 3 4 echo $RANDOM echo $[RANDOM%7] 随机7个数(0-6)echo $[$[RANDOM%7]+31] 随机7个数(31-37)
3、echo打印颜色字
1 2 3 4 5 6 echo -e "\033[31malong\033[0m" 显示红色alongecho -e "\033[1;31malong\033[0m" 高亮显示红色alongecho -e "\033[41malong\033[0m" 显示背景色为红色的alongecho -e "\033[31;5malong\033[0m" 显示闪烁的红色alongcolor=$[$[RANDOM%7]+31] echo -ne "\033[1;${color} ;5m*\033[0m" 显示闪烁的随机色along
几个有意思的脚本 1、9x9乘法表
1 2 3 4 5 6 7 #!/bin/bash for a in {1..9};do for b in `seq 1 $a `;do let c=$a *$b ;echo -e "${a} x${b} =$c \t\c" done echo done
2、彩色等腰三角形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #!/bin/bash read -p "Please input a num: " numif [[ $num =~ [^0-9] ]];then echo "input error" else for i in `seq 1 $num ` ;do xing=$[2*$i -1] for j in `seq 1 $[$num -$i ]`;do echo -ne " " done for k in `seq 1 $xing `;do color=$[$[RANDOM%7]+31] echo -ne "\033[1;${color} ;5m*\033[0m" done echo done fi
3、国际象棋棋盘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #!/bin/bash red="\033[1;41m \033[0m" yellow="\033[1;43m \033[0m" for i in {1..8};do if [ $[i%2] -eq 0 ];then for i in {1..4};do echo -e -n "$red$yellow " ; done echo else for i in {1..4};do echo -e -n "$yellow$red " ; done echo fi done