==条件判断 if==

语法格式:

单分支

if 判断条件;then
    条件为真的分支代码
fi

双分支

if 判断条件;then
    条件为真的分支代码
else
    条件为假的分支代码
fi

多分支

if 判断条件1;then
        条件为真的分支代码
    elif 判断条件2;then
        条件为真的分支代码
    elif 判断条件3;then
        条件为真的分支代码
    else
        以上条件都为假的分支代码
fi

逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句

示例

#!/bin/bash
read -p "请输入你的年龄:  " age
if [[ ! "$age" =~ ^[0-9]+$ ]];then
echo "请输入数字"
exit 2
elif [ "$age" -le 18 ]; then
echo "你的年龄小于等于18"
elif [ "$age" -le 60 ];then
echo "你的年龄大于18,小于等于60"
else
echo "你太老了,可以退休了"
fi

判断分数

#!/bin/bash
read -p "请输入判断分数:" fenshu
if [[ ! "$fenshu" =~ ^([0-9]|[1-9][0-9]|100)$ ]];then
echo "请输入0-100的数字,重新运行脚本"
exit 2
elif [ "$fenshu" -le 59 ];then
echo "不合格"
elif [ "$fenshu" -le 80 ];then
echo "良好"
elif [ "$fenshu" -le 100 ];then
echo "优秀"
fi

==条件判断 case==

适合判断离散值,比如,1,3,9

语法格式:

可以有多种格式

case支持glob风格的通配符:

  • [ ] *: 任意长度任意字符
  • [ ] ?: 任意单个字符
  • [ ] []:指定范围内的任意单个字符
  • [ ] a|b: a或b
case 变量引用 in
        PAT1)
            分支1
            ;;
        PAT2)
            分支2
            ;;
    ...这里可以有多个
            *)
            默认分支
            ;;
esac

示例

#!/bin/bash
read -p "输入 yes/no " word
case $word in
[Yy]|[Yy][Ee][Ss])
    echo "你输入的是$word,等同yes"
    ;;
[Nn]|[Nn][Oo]) 
    echo "你输入的是$word,等同于no"
    ;;
*)
    echo "你输入的是$word,不是指定的值"
    ;;
esac

练习

1、编写脚本createuser.sh,实现如下功能:使
用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息

#!/bin/bash
#定义变量
_user=`grep -wq ^$1 /etc/passwd;echo $?`
#判断是否有输入参数
if [ $# -eq 0 ];then
    echo "你没有输入参数,语法:$0 username"
    exit 10
    #判断参数指定的用户是否存在
    elif [  "$_user" -eq 0 ];then
        echo "用户存在,脚本退出"
        exit 20
    else
        echo "用户不存在,将会创建用户$1,并显示用户信息"
        useradd $1 && id $1
fi

2、编写脚本yesorno.sh,提示用户输入yes或no,
并判断用户输入的是yes还是no,或是其它信息

#!/bin/bash
read -p "请输入yes 或 no: " word
case $word in
[Yy]|[Yy][Ee][Ss])
    echo "你输入的是$word,等价于yes"
    ;;
[Nn]|[Nn][Oo])
    echo "你输入的是$word,等价于no"
    ;;
*)
    echo "你输入的是其他字符"
    ;;
esac

3、编写脚本filetype.sh,判断用户输入文件路径
显示其文件类型(普通文件,目录,链接,其它文件类型)

#!/bin/bash
read -p "请输入一个文件路径: " path
if [ ! -e "$path" ];then
        echo "你所输入文件不存在"
    elif [ -f "$path" ];then
        echo "你输入的是普通文件"
    elif [ `file "$path"|grep -o 'symbolic'` = "symbolic" ];then
        echo "你输入的是链接文件"
    elif [ -d "$path" ];then
        echo "你输入的是一个目录"
    else
        echo "你输入的是其他文件类型"
fi

[ -L $path] 是判断:存在且为符号链接文件,判断的结果是以链接文件背后的指向为准,也就是说,如果符号链接实际指向的一个目录的话,这具判断的结果就是一个目录,而不是本身符号链接
所以上面的例子使用的是file来判断。
比如这个文件:

lrwxrwxrwx.  1 root root    6 Dec 14 05:51 /var/run -> ../run
drwxr-xr-x   24 root root  740 Dec 26 14:38 /run

4、编写脚本checkint.sh,判断用户输入的参数是否为正整数

#!/bin/bash
_rege=^[1-9][0-9]*$
if [[ $1 =~ $_rege ]];then
        echo -e "你输入的第一个参数\033[1;35m是正整数\033[0m"
    else
        echo -e "你输入的第一个参数\033[1;31m不是正整数\033[0m"
fi

上面的例子用到了正则表达式,注意了 =~ 右边不允许出现引号。echo -e 是启用后面的颜色转义。


==循环==

循环执行

  1. 将某代码段重复运行多次

  2. 重复运行多少次:

    • [ ] 循环次数事先已知
    • [ ] 循环次数事先未知
  3. 有进入条件和退出条件

==for循环==

语法格式:

第一种

for 变量名 in 列表;do
    循环体
done
  • [x] 执行机制:

依次将列表中的元素赋值给“变量名” ; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束。

列表生成方式:

  • [ ] (1) 直接给出列表
  • 如:for i in 1 2 3
  • [ ] (2) 整数列表:
    • [ ] (a) {start..end}
    • 如:for i in {a..z}
    • [ ] (b) $(seq [start [step]] end)
    • 如:for i in $(seq 1 2 10)
  • [ ] (3) 返回列表的命令$(COMMAND)
    • 如:for i in $(seq 1 10)
  • [ ] (4) 使用通配符
    • 如:for i in /app/shell/*.sh
  • [ ] (5) 变量引用;$@, $*
    • 如:for i in $@

示例

把1-100相加的结果显示出来

#!/bin/bash
sum=0
for i in {1..100};do
    sum=$[sum+i]
done
echo $sum

语法格式:(第二种) C语言风格

for ((控制变量初始化;条件判断表达式;控制变量的修正表达式));do
    循环体
done

 控制变量初始化:仅在运行到循环代码段时执行一次
 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断

for (( exp1; exp2; exp3 ));do
    COMMANDS;
done

相当于
        (( EXP1 ))
        while (( EXP2 )); do
            COMMANDS
            (( EXP3 ))
        done

示例


#/bin/bash
for ((  i=0;i<=100;i++ ));do
sum=$[sum+i]
done
echo "$sum"

运行结果

#bash test.sh 
5050

C语言风格的 for 允许使用多个迭代变量,循环会单独处理每个变量。可以为每个变量定义不同的迭代过程。尽管可以使用多个变量,但只能在for循环中定义一种条件。

#!/bin/bash
for (( i=1,b=10; i<=10;i++,b--  ));do
        echo "$i - $b"
done

#bash IFS.sh 
1 - 10
2 - 9
3 - 8
4 - 7
5 - 6
6 - 5
7 - 4
8 - 3
9 - 2
10 - 1
> 练习

1、把100以内不能被3整除的数显示出来,并相加这些数字显示计算结果

#!/bin/bash
for i in {1..100};do
if [ "$[i%3]" -ne 0 ];then
echo -n "$i "
let sum+=i
fi
done
echo -e "\n100以内不能被3整除的数相加的结果是:$sum"


运行结果

1 2 4 5 7 8 10 11 13 14 16 17 19 20 22 23 25 26 28 29 31 32 34 35 37 38 40 41 43 44 46 47 49 50 52
53 55 56 58 59 61 62 64 65 67 68 70 71 73 74 76 77 79 80 82 83 85 86 88 89 91 92 94 95 97 98 100 
100以内不能被3整除的数相加的结果是:3367


2、判断/var/目录下所有文件的类型

#!/bin/bash
_path=/var/*
for i in $_path;do
echo "$i的文件类型是:stat -c %F $i"
done

运行结果

#bash checkfile.sh 
/var/adm的文件类型是:directory
/var/cache的文件类型是:directory
/var/crash的文件类型是:directory
/var/db的文件类型是:directory
/var/empty的文件类型是:directory
/var/games的文件类型是:directory
/var/gopher的文件类型是:directory
/var/kerberos的文件类型是:directory
/var/lib的文件类型是:directory
/var/local的文件类型是:directory
/var/lock的文件类型是:symbolic link
/var/log的文件类型是:directory
/var/mail的文件类型是:symbolic link
/var/nis的文件类型是:directory
/var/opt的文件类型是:directory
/var/preserve的文件类型是:directory
/var/run的文件类型是:symbolic link
/var/spool的文件类型是:directory
/var/tmp的文件类型是:directory
/var/www的文件类型是:directory
/var/yp的文件类型是:directory

stat -c 后面可以按指定格式输出文件属性信息

3、批量添加linux账号:10个用户user1-user10,密码为8位随机字符,并且把密码保存到passwd.txt文件内,文件内容格式为:user1:passwd1

以下方法都可以取随机数,请参考:
#cat /dev/urandom |tr -dc 'a-zA-Z0-9'|head -c8
#openssl rand -base64 10|head -c8

#!/bin/bash
#定义了记录密码文件的变量
_passwdfile=/app/passwd.txt
for i in {1..10};do
#创建用户并将创建的用户名写入指定文件
useradd user$i
echo -n "user$i:" >> $_passwdfile
#算出随机数并交给passwd更新用户密码
echo "openssl rand -base64 10|head -c8" | tee -a $_passwdfile | passwd --stdin user$i &> /dev/null
echo "用户user$i创建完毕,并设定了随机8位密码,密码记录在:$_passwdfile"
done

运行结果

#cat /app/passwd.txt 
user1:y70D9Jp7
user2:Ykseahss
user3:pSFLZ5bP
user4:9NNjOy4M
user5:LSiIB8IX
user6:0aGjxkWN
user7:pAvtRQkT
user8:GimQuuYN
user9:yR4YJR+U
user10:BxzFRlSW

tee命令用于将上一条命令的stdout重定向到文件,并且重定向副本作为后续命令的stdin
-a:向文件中重定向时使用追加模式,相当于重定向的 >>

4、 /etc/rc.d/rc3.d目录下分别有多个以K开头和以S开头的文件;分别读取每个文件,以K开头的输出为文件加stop,以S开头的输出为文件名加start,如K34filename stop;   S66filename start

#使用if的方法

#!/bin/bash
_path=/etc/rc.d/rc3.d/
for i in $_path;do
if [[ basename $i =~ ^K.
 ]];then
echo "$i stop"
elif [[ basename $i =~ ^S.* ]];then
echo "$i start"
fi
done

#使用case的方法

#!/bin/bash
_path=/etc/rc.d/rc3.d/
for i in $_path;do
case basename $i in
K
)
echo "basename $i stop"
;;
S*)
echo "basename $i start"
;;
esac
done

运行结果

#bash checkrcd.sh 
/etc/rc.d/rc3.d/K50netconsole stop
/etc/rc.d/rc3.d/S10network start

5、编写脚本,提示输入正整数num的值,计算从1到num的总和

#!/bin/bash
#判断用户输入的是否正整数
read -p "请输入正整数num的值,脚本将会计算从1到num的总和并显示: " num
if [[ ! $num =~ ^[1-9][0-9]*$ ]];then
echo "输入的不是正整数,正整数的范围是1-∞,脚本将会退出"
exit 10
else
unset sum
for i in seq 1 "$num";do
let sum+=$i
done
fi
echo "从1到num的总和 是:$sum"

6、编写脚本ip扫描器,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态

#!/bin/bash
#定义了IP地址中,网络地址的格式,如 192.168.0
regex='\<(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){2}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>'
#定义在线主机存放文件
_onlineip=/app/online_ip.txt
#定义不在线主机存放文件
_offlineip=/app/offline_ip.txt

#每次扫描前清空上次存放的记录

$_onlineip
$_offlineip
read -p "请输入要扫描的网络地址,如 192.168.0 " ipnet
#判断输入的网络地址是否合法
if [[ ! "$ipnet" =~ $regex ]];then
echo "输入的网络地址不合法,脚本将退出"
exit 10
else
for i in {1..254};do
#这里使用{ }是为了并行处理,提高效率
{
if ping -c 2 -w 2 $ipnet.$i &> /dev/null;then
echo "IP $ipnet.$i 在线"
echo "$ipnet.$i" >> $_onlineip
else
echo "IP $ipnet.$i 不在线"
echo "$ipnet.$i" >> $_offlineip
fi
} &
done
wait
fi

运行结果

#bash scanip.sh
请输入要扫描的网络地址,如 192.168.0 192.168.4
IP 192.168.4.1 在线
IP 192.168.4.100 在线
IP 192.168.4.101 在线
IP 192.168.4.53 不在线
IP 192.168.4.31 不在线
IP 192.168.4.128 不在线
如果不用并发处理,看到的顺序会从1~254,不过就太慢了。
wait指令可以让后台执行程序执行完成后退出

7、打印矩形

#!/bin/bash
_line=4
_colum=8
for i in seq $_line;do
for j in seq $_colum;do
echo -n "*"
done
echo
done

运行结果

#bash juxing.sh





8、打印九九乘法表

语法1:
#/bin/bash
#循环行数
for i in {1..9};do
#循环列的数量,等于行数
for j in seq $i;do
echo -ne "$j X $i = $[j*i]\t"
done
#输出一个新行,便于每一行之后换行
echo
done

运行结果

#bash 99.sh 
1 X 1 = 1 
1 X 2 = 2 2 X 2 = 4 
1 X 3 = 3 2 X 3 = 6 3 X 3 = 9 
1 X 4 = 4 2 X 4 = 8 3 X 4 = 12 4 X 4 = 16 
1 X 5 = 5 2 X 5 = 10 3 X 5 = 15 4 X 5 = 20 5 X 5 = 25 
1 X 6 = 6 2 X 6 = 12 3 X 6 = 18 4 X 6 = 24 5 X 6 = 30 6 X 6 = 36 
1 X 7 = 7 2 X 7 = 14 3 X 7 = 21 4 X 7 = 28 5 X 7 = 35 6 X 7 = 42 7 X 7 = 49 
1 X 8 = 8 2 X 8 = 16 3 X 8 = 24 4 X 8 = 32 5 X 8 = 40 6 X 8 = 48 7 X 8 = 56 8 X 8 = 64 
1 X 9 = 9 2 X 9 = 18 3 X 9 = 27 4 X 9 = 36 5 X 9 = 45 6 X 9 = 54 7 X 9 = 63 8 X 9 = 72 9 X 9 = 81

语法2:
#/bin/bash
for (( i=1;i<=9;i++ ));do
for (( j=1;j<=i;j++ ));do
echo -ne "${j}x${i}=$[j*i]\t"
done
echo
done

运行结果

#bash 99-new.sh 
1x1=1 
1x2=2 2x2=4 
1x3=3 2x3=6 3x3=9 
1x4=4 2x4=8 3x4=12 4x4=16 
1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 
1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 
1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 
1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 
1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81

9、在/tmp目录下创建10个html文件,文件名格式为数字N(从1到10)加随机8个字符,如:1AbCdeFgH.html

#!/bin/bash
for i in {1..10};do
echo " 创建了 mktemp --suffix=.html -p /tmp/ ${i}XXXXXXXX"
done

运行结果

#ls /tmp/
1tKMzfFfVx.html 2GvLrwZHn.html 4ms1ildS9.html 6ZwlWhu02.html 8k49voEHW.html
10FJDRpge.html 3khkjI5vu.html 5uOIumnYP.html 7pYy7KHuN.html 9QDJaNxg2.html

mktemp用于生成指定数量的随机临时文件。XXX是指定具体数量的随机符号,3个XXX就是3个,
--suffix=.html,用于指定后缀。
-p 用于指定目录

#使用openssl rand 生成随机数方法
#!/bin/bash
for i in {1..10};do
touch /tmp/$iopenssl rand -base64 12|tr -dc '[:alnum:]'|head -c8.html
done

#使用cat /dev/urandom生成随机数方法
#!/bin/bash
for i in {1..10};do
touch /tmp/$icat /dev/urandom|tr -dc '[:alnum:]'|head -c8.html
done

至于效率哪个高,请使用time 脚本进行测试吧,工作中按需使用。

10、打印等腰三角形

要点:
先打空格,再打星星
每行打印的空格数=总行数-当前行数
每行打印的星星数=当前行数 X 2 -1

#/bin/bash
read -p "请输入总的行数:" topline
if [[ ! "$topline" =~ ^[1-9][0-9]$ ]];then
echo "你输入的不是正整数"
exit 10
fi
#循环总数
for i in seq $topline;do
#每行打印的空格数=总行数-当前行数
for j in seq $[$topline-$i];do
echo -n " "
done
#每行打印的星星数=当前行数x2-1 
for k in `seq $[$i
2-1]`;do
echo -n "*"
done
#打印换行,进行下一轮循环
echo
done

运行结果

#bash sanjiao.sh
请输入总的行数:5
*





11、打印闪烁的三角形

#/bin/bash
#定义外层星星颜色
_color1="\033[1;5;$[$RANDOM%7+31]m"
_color2="\033[0m"
#定义内层星星颜色
_colorstar1="\033[1;$[$RANDOM%6+31]m*\033[0m"

read -p "请输入总的行数:" topline
if [[ ! "$topline" =~ ^[1-9][0-9]*$ ]];then
echo "你输入的不是正整数"
exit 10
fi
#循环总数
for i in seq $topline;do
#每行打印的空格数=总行数-当前行数
for j in seq $[$topline-$i];do
echo -n " "
done
#打印数字1的边
if [ "$i" -ne 1 ];then
echo -en "${_color1}1$_color2"
fi

#每行打印的内圈星星数=(当前行数-1)x2-1    
for k in `seq $[$[$i-1]*2-1]`;do
#打印数字2的边
    if [ "$i" -eq "$topline"  ];then
            echo -en "${_color1}2$_color2"
        else
            echo -en "$_colorstar1"
    fi
done
#打印数字3的边

echo -e "${_color1}3$_color2"
done

运行结果

#bash colorsanjiao.sh
请输入总的行数:12
3
1*3
1*3
1***
3
1*3
1***
3
1*3
1***
3
1*3
1***
3
1***3
12222222222222222222223
唉,这微博的编辑器让我原来的排版不美观了。希望不影响各位

![实际运行图](http://i2.51cto.com/images/blog/201712/28/2e8db0cbce2dc518f7a97d58c85cc03a.gif)
12、打印棋盘

#b.sh

#/bin/bash
#定义颜色开关
_cr1="\033[1;"
_cr2="m \033[0m"

####################################################
num=4
for i in seq $num;do
#打印列颜色方格
for j in seq $num;do
echo -e "${_cr1}43${_cr2}\c"
echo -e "${_cr1}44${_cr2}\c"
done
echo

#打印行颜色方格
for k in seq $num;do
echo -e "${_cr1}44${_cr2}\c"
echo -e "${_cr1}43${_cr2}\c"
done
echo
done

a.sh

#/bin/bash
line=8
line2=$[line*2]
for i in seq 1 8 ; do
for j in seq 1 $line2 ; do
if [ $[i%2] -eq 1 ] ; then
if [ $[j%4] -eq 1 -o $[j%4] -eq 2 ] ; then
echo -ne "\033[41m \033[0m"
else
echo -ne "\033[42m \033[0m"
fi
else
if [ $[j%4] -eq 1 -o $[j%4] -eq 2 ] ; then
echo -ne "\033[42m \033[0m"
else
echo -ne "\033[41m \033[0m"
fi
fi
done
echo
done

2种代码,第一种比较有效率
![](http://i2.51cto.com/images/blog/201712/29/0571278ba90da7d04cfcb698fa003532.jpg?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)
各自代码块for循环100次,得出对比结果

a.sh
real 0m2.928s
user 0m1.012s
sys 0m1.958s

b.sh
real 0m2.622s
user 0m0.688s
sys 0m1.973s

a.sh
real 0m3.188s
user 0m1.132s
sys 0m2.070s

b.sh
real 0m2.571s
user 0m0.669s
sys 0m1.947s

a.sh
real 0m2.867s
user 0m0.956s
sys 0m1.961s

b.sh
real 0m2.217s
user 0m0.544s
sys
最后一个可以自定义颜色和方格数的

#/bin/bash
#定义颜色开关
_cr1="\033[1;"
_cr2="m \033[0m"

echo -e "\033[1;5;31m颜色列表\033[0m
40=black${_cr1}40${_cr2} 41=red${_cr1}41${_cr2} 42=green${_cr1}42${_cr2} 43=yellow${_cr1}43${_cr2} 44=blue${_cr1}44${_cr2} 45=magenta${_cr1}45${_cr2} 46=cyan${_cr1}46${_cr2} 47=white${_cr1}47${_cr2}
"

##   生成基本数据     ###############################

echo "请选择国际象棋盘颜色1"
read -p "请输入颜色列表中的数字:" colorA
if [[ ! "$colorA" =~ ^4[0-7]$ ]];then
    echo "输入的颜色代码不正确"
    exit 10
fi

echo "请选择国际象棋盘颜色2"
read -p "请输入颜色列表中的数字:" colorB
if [[ ! "$colorB" =~ ^4[0-7]$ ]];then
    echo "输入的颜色代码不正确"
    exit 20
fi

read -p "请输入棋盘方格大小:" num
if [[ ! "$num" =~ ^[1-9][0-9]*$ ]];then
    echo "请输入1以上的正整数"
    exit 30
fi

####################################################

for i in `seq $num`;do
#打印列颜色方格
    for j in `seq $num`;do
        echo -e "\033[${colorA}m  \033[0m\c"
        echo -e "\033[${colorB}m  \033[0m\c"
    done
    echo

#打印行颜色方格
    for k in `seq $num`;do
        echo -e "\033[${colorB}m  \033[0m\c"
        echo -e "\033[${colorA}m  \033[0m\c"
    done
    echo
done

截图:
Shell 编程进阶(一)