二、使用结构化命令
知识内容:
# 改变命令流
# 使用if-then逻辑
# 嵌套if-then
# 测试条件
# 高级if-then功能
许多程序在脚本命令之间需要某些逻辑控制流,有些命令允许脚本根据变量值的条件或者命令的结果跳过一些命令或者循环执行这些命令,这叫做结构化命令。
1、使用if-then语句
最基本的结构化命令类型就是if-then语句,其格式如下:
if command
then   
    command
fi
意思是说:if语句后面的命令的退出状态值是0,则执行then后面的所有命令;如果不是0则命令不执行。如下例子:
[root@wzp ~]# cat test2
#!/bin/bash
if date
then
        echo "it works"
fi
[root@wzp ~]# ./test2
2011年 01月 28日 星期五 21:15:12 CST
it works
我们知道date命令是有效命令,查看$?肯定是0的,所以就执行then后的命令,这个应该不难理解。
2、if-then-else语句
这个语句可以提供一组命令,其格式如下:
if command
then
    command
else
    command
if
意思是说:if语句后面的命令的退出状态值是0,则执行then后面的所有命令;跟if-then一样,如果不是则执行else后面的命令。如下例子:
[root@wzp ~]# cat test2
#!/bin/bash
testuser=51cto
if grep $testuser /etc/passwd
then
        echo  the files for $testuser are:
        ls -a /home/$testuser/.*
else
        echo the user name $testuser does not exist !
fi
[root@wzp ~]# ./test2
the user name 51cto does not exist !
从上面可以看到由于不存在51cto用户,则执行else后面的命令
但是当我创建了51cto用户,则:
[root@wzp ~]# useradd 51cto
[root@wzp ~]# ./test2
51cto:x:502:502::/home/51cto:/bin/bash
the files for 51cto are:
/home/51cto/.bash_logout   /home/51cto/.bashrc
/home/51cto/.bash_profile  /home/51cto/.emacs

/home/51cto/.:
.  ..  .bash_logout  .bash_profile  .bashrc  .emacs  .mozilla

/home/51cto/..:
.  ..  51cto  aa  bb  lost+found

/home/51cto/.mozilla:
.  ..  extensions  plugins
直接执行then后的命令!这个也容易理解!~
3、嵌套if语句
有时需要在脚本代码中检查几种情况,可以使用else的另一种版本叫elif,但是要嵌套许多if-then语句,后续有case命令介绍,这个更加的方便使用,这里就不对嵌套if语句详解了,其格式是:
if command
then
    command
elif    command
then
       command
fi
4、test命令
这是一个重头戏,bash shell提供一种在if-then语句中声明test命令的另一种方法:
if [ condition ]  //注意这里的condition两边有空格!切记!
then
    command
fi
test命令能够评估以下3类命令:
# 数值比较
# 字符串比较
# 文件比较
下面就具体就test命令的用户逐一说明!!!
4.1、数值比较
如下式数值比较表:
**********************************************
n1  -eq  n2    检查n1是不是等于n2
n1  -ge  n2    检查n1是不是大于或等于n2
n1  -gt  n2    检查n1是否大于n2
n1  -le  n2    检查n1是否小于或等于n2
n1  -lt  n2    检查n1是否小于n2
n1  -ne  n2    检查n1是否不等于n2
**********************************************
下面看看一个例子:
[root@wzp ~]# cat test2
#!/bin/bash
num1=10
num2=11
if [ $num1 -gt 5 ]
then
  echo the test value $num1 is greater than 5
fi

if [ $num2 -eq $num1 ]
then
  echo  the values are equal
else
  echo  the values are different
fi
[root@wzp ~]# ./test2
the test value 10 is greater than 5
the values are different
给num1和num2赋值,然后通过测试,得出不同结论,这个好理解!
4.2、字符串比较
test命令允许对字符串值进行比较,主要分为如下三种比较类型:
1、字符串是否相同
2、字符串顺序(大小)
3、字符串长度
首先也先看看test命令字符串比较表
********************************************
str1 = str2   检查str1和str2是否相同
str1 != str2  检查str1和str2是否不同
str1 < str2   检查str1是否小于str2
str1 > str2   检查str1是否大于str2
  -n str1     检查str1长度是否大于0
  -z str1     检查str1长度是否为0
********************************************
下面对三种类型举个例子:
1)字符串是否相等
[root@wzp ~]# cat test2
#!/bin/bash
testuser=root
if [ $USER=$testuser ]
then
  echo welcome $testuser
fi
[root@wzp ~]# ./test2
welcome root
可以看到字符串相等的显示效果
[root@wzp ~]# cat test2
#!/bin/bash
testuser=root
if [ $USER!=$testuser ]
then
  echo this is not $testuser
else
  echo welcome $testuser
fi
[root@wzp ~]# ./test2
this is not root
不相等的情况则显示then后的内容
2)字符串顺序
[root@wzp ~]# cat test2
#!/bin/bash
str1=aaa
str2=bbb
if [ $str1 > $str2 ]
then
  echo $str1 is greater than $str2
else
  echo $str1 is less than $str2
fi
[root@wzp ~]# ./test2
aaa is greater than bbb
[root@wzp ~]# ll aaa
-rw-r--r-- 1 root root 0 01-28 22:22 aaa
在脚本中单独使用了大于号>,虽然没报错,但结果是错误的,a打头肯定是小于b的。这个脚本把大于号理解成输出重定向,所以才出现这样的情况,我们可以通过转义大于号解决这个问题:
[root@wzp ~]# cat test2
#!/bin/bash
str1=aaa
str2=bbb
if [ $str1 \> $str2 ]     //此处添加一个\就行了
then
  echo $str1 is greater than $str2
else
  echo $str1 is less than $str2
fi
[root@wzp ~]# ./test2
aaa is less than bbb
这样就得出了正确的结果!
还有一点要说明的就是大小写的问题,它的顺序跟sort命令的顺序是相反的!test命令是通过ASCII数值来决定排列顺序的,这个稍微了解下就好。
3)字符串大小
评估一个变量是否包含数据,通过使用-n和-z比较很方便,如下例子:
[root@wzp ~]# cat test2
#!/bin/bash
str1=aaa
str2=
if [ -n $str1 ]    //长度是否大于0
then
        echo the string $str1 is not empty
else
        echo the string $str1 is empty
fi
if [ -z $str2 ]     //长度是否为0
then
        echo the string $str2 is empty
else
        echo the string $str2 is not empty
fi
if [ -z $str3 ]     //长度是否为0
then
        echo the string $str3 is empty
else
        echo the string $str3 is not empty
fi
[root@wzp ~]# ./test2
the string aaa is not empty
the string is empty
the string is empty
如上对于str1和str2应该没什么问题,而对于没有定义变量str3则认定其字符串为零。
4.3、文件比较
test命令能够检测linux文件系统上的文件状态和路径,对于文件比较这一块挺多东西的,下面一一道来,首先看看test命令文件比较表:
****************************************************************
-d file            检测file是否存在并且是一个目录
-e file            检测file是否存在
-f file            检测file是否存在并且是一个文件
-r file            检测file是否存在并且刻度
-s file            检测file是否存在并且不为空
-w file            检测file是否存在并且可写
-x file            检测file是否存在并且可执行
-O file            检测file是否存在并且被当前用户拥有
-G file                检测file是否存在并且默认组是否为当前用户组
file1 -nt file2        检测file1是否比file2新
file1 -ot file2        检测file1是否比file2旧
*****************************************************************
对于上面这个表,如下通过10个例子说明:
1)检测目录
[root@wzp ~]# cat test2
#!/bin/bash
if [ -d $HOME ]
then
  echo your home directory exists
  cd $HOME
  ls
else
  echo there is someting wrong with your HD
fi
[root@wzp ~]# ./test2
your home directory exists
aaa              bbb      install.log         mbox  test2
anaconda-ks.cfg  Desktop  install.log.syslog  one
使用-d把我这个root的家目录ls出来了~~
2)检测对象是否存在
[root@wzp ~]# cat test2
#!/bin/bash
if [ -e $HOME ]
then
  echo   yes, the HD exists
  if [ -e $HOME/bbs.51cto ]
  then
    echo the bbs.51cto exists
  else
    touch $HOME/bbs.51cto
    echo creating new file name bbs.51cto
  fi
fi
从上面我们看到目录肯定是存在的,我的root嘛,但是这个bbs.51cto是不存在的,所以第一次执行文件显示不存在,并且私下创建了。
[root@wzp ~]# ./test2
yes, the HD exists
creating new file name bbs.51cto
第二次执行文件那肯定是显示存在bbs.51cto了,呵呵
[root@wzp ~]# ./test2
yes, the HD exists
the bbs.51cto exists
3)检测文件
-e适合用于检测文件和目录,要确定对象是文件,使用-f比较,下例:
[root@wzp ~]# cat test2
#!/bin/bash
if [ -e $HOME ]
then
  echo the $HOME exists !
  if [ -f $HOME ]
  then
    echo yes, it is a file
  else
    echo no, it is not a file
        if [ -f $HOME/.bash_history ]
        then
          echo but $HOME/.bash_history is a file
        else
          echo it is not a file too
        fi
  fi
else
  echo there is not object!
fi
[root@wzp ~]# ./test2
the /root exists !
no, it is not a file
but /root/.bash_history is a file
上面的例子应该很好懂,注意-f是检测文件,-e可以检测文件和目录就行了!
4)检测是否可读
通过-r可检测可读性,如下例:
[51cto@wzp ~]$ whoami
51cto
[51cto@wzp ~]$ cat test
#!/bin/bash
testfile=/etc/shadow
if [ -f $testfile ]
then
  if [ -r $testfile ]
  then
    ls -l $testfile
  else
    echo "i'm unable to read the file"
  fi
else
  echo "the file doesn't exist"
fi
[51cto@wzp ~]$ sh test
i'm unable to read the file
[51cto@wzp ~]$ su - root
口令:
[root@wzp ~]# sh /home/51cto/test
-r-------- 1 root root 1204 01-28 21:27 /etc/shadow
如上可知普通用户51cto无法读取文件。
5)检测空文件
通过-s检测文件是否为空,例子如下:
[root@wzp ~]# cat test2
#!/bin/bash
file=testfile
touch $file
if [ -s $file ]
then
  echo the file exists and has data in it
else
  echo the file exists but empty
fi
date > $file
if [ -s $file ]
then
  echo the file exists and has data in it
else
  echo the file exists but empty
fi
[root@wzp ~]# ./test2
the file exists and has data in it
the file exists and has data in it
先是创建空文件,然后通过重定向date进去判断文件不为空,这个好理解!
6)检测是否能够可写
通过-w检测文件是否可写,例子如下:
[root@wzp ~]# cat test2
#!/bin/bash
file=$HOME/testfile
touch $file
chmod u-w $file
now='date +%y%m%d-%H%M'
if [ -w $file ]
then
  echo the file could be written
else
  echo "the file couldn't be written"
fi
chmod u+w $file
if [ -w $file ]
then
  echo the file could be written
  $now > $HOME/testfile
  echo  and the file views $file
else
  echo "the file couldn't be written"
fi
[root@wzp ~]# ./test2
the file could be written
the file could be written
and the file views /root/testfile
[root@wzp ~]# cat /root/testfile
110129-1317
上面的例子相等好理解,没什么好说的。
7)检测是否能运行文件
通过-x可以检测文件是否可被执行,如下例:
[root@wzp ~]# cat test2
#!/bin/bash
file=$HOME/testfile2
touch $file
chmod u+x $file
if [ -x $file ]
then
  echo the file could be run
else
  echo "the file couldn't be run"
fi
[root@wzp ~]# ./test2
the file could be run
[root@wzp ~]# ll testfile2
-rwxr--r-- 1 root root 0 01-29 13:21 testfile2
这个也没什么疑惑之处。
8)检测所有者
通过-O可以检测你是否属于这个文件的所有者,如下例:
[root@wzp ~]# cat test2
#!/bin/bash
file=/etc/passwd
if [ -O $file ]
then
  echo "you are the owner of the $file"
else
  echo "you aren't the owner of the $file"
fi
[root@wzp ~]# ./test2
you are the owner of the /etc/passwd
[root@wzp ~]# ll /etc/passwd
-rw-r--r-- 1 root root 1877 01-28 21:27 /etc/passwd
/etc/passwd属有者肯定是root啦!
9)检测所属组
-G检测文件的默认组,如果它跟当前用户的默认组匹配,则检测成功,如下例:
[root@wzp ~]# cat test2
#!/bin/bash
file=/etc/passwd
if [ -G $file ]
then
  echo "you are in the same group as $file"
else
  echo "you aren't in the same group as the $file"
fi
[root@wzp ~]# ./test2
you are in the same group as /etc/passwd
[root@wzp ~]# ll -d /etc/passwd
-rw-r--r-- 1 root root 1877 01-28 21:27 /etc/passwd
很明显,/etc/passwd肯定属于root组咯
[root@wzp ~]# chgrp 51cto /etc/passwd
[root@wzp ~]# ll -d /etc/passwd
-rw-r--r-- 1 root 51cto 1877 01-28 21:27 /etc/passwd
[root@wzp ~]# ./test2
you aren't in the same group as the /etc/passwd
当把这个文件的所属组改成51cto后,则检测不成功了,不过现实中别做这样的修改
10)检测文件日期
一般通过-nt和-ot来比较两个文件之间的新旧,这里指的是创建或修改日期,例子:
[root@wzp ~]# ll aa bb
-rw-r--r-- 1 root root 0 01-29 13:41 aa
-rw-r--r-- 1 root root 0 01-29 14:23 bb
aa
[root@wzp ~]# cat test2
#!/bin/bash
if [ $HOME/aa -nt $HOME/bb ]
then
  echo the aa is newer than bb
else
  echo the aa is older than bb
fi
[root@wzp ~]# ./test2
the aa is older than bb
下面我把aa文件修改下内容:
[root@wzp ~]# vim aa
[root@wzp ~]# ll aa bb
-rw-r--r-- 1 root root 3 01-29 14:28 aa
-rw-r--r-- 1 root root 0 01-29 14:23 bb
[root@wzp ~]# ./test2
the aa is newer than bb
这个结果应该很容易接受!
OKOK,到这里test命令的内容就结束了,如上各种类型比较得以说明了。
5、复合条件检测
if-then语句可以使用布尔逻辑来合并检测条件,格式:
*****************************************************
[ condition ] && [ condition ]   这个表示逻辑与 and
[ condition ] || [ condition ]   这个表示逻辑或 or
*****************************************************
如下例子:
[root@wzp ~]# cat test2
#!/bin/bash
if [ -d $HOME ] && [ -w $HOME/wzp ]
then
  echo the file exists and you can wirte $HOME/wzp
else
  echo "you can't write the file"
fi
if [ -d $HOME ] || [ -w $HOME/wzp ]
then
  echo the file exists and you can wirte $HOME/wzp
else
  echo "you can't write the file"
fi
[root@wzp ~]# ./test2
you can't write the file
the file exists and you can wirte /root/wzp
[root@wzp ~]# ll /root/wzp
ls: /root/wzp: 没有那个文件或目录
$HOME是root家目录,肯定存在的,但是不存在文件/root/wzp
6、if-then的高级特征
既然说是高级特征,必然作用非凡了,呵呵
其功能体现是通过双圆括号(())表示数学表达式和双方括号[[]]表示高级字符串处理函数。
6.1、使用双圆括号
先看一个例子:
[root@wzp ~]# cat test2
#!/bin/bash
num1=10
if (( $num1 ** 2 > 90 ))
then
  (( num2=$num1 ** 2 ))
  echo the square of $num1 is $num2
fi
[root@wzp ~]# ./test2
the square of 10 is 100
这里的**表示取幂,由此可见(())可以很方便处理数学表达式
6.2、使用双方括号
使用双方括号可以定义与字符串值相匹配的正则表达式,如例:
[root@wzp ~]# cat test2
#!/bin/bash
if [[ $USER == r* ]]
then
  echo  hello $USER
else
  echo sorry , i "don't" know you
fi
[root@wzp ~]# ./test2
hello root
[root@wzp ~]# cp test2 /home/51cto/
[root@wzp ~]# su - 51cto
[51cto@wzp ~]$ sh test2
sorry , i don't know you
这里用到了通配符*,表示r开头的任何用户,正则表达式的内容放置后面说明!
[root@wzp ~]# useradd rrr
[root@wzp ~]# cp test2 /home/rrr
[root@wzp ~]# su - rrr
[rrr@wzp ~]$ sh test2
hello rrr
这都好理解吧~~~
7、case命令
还记得前面提及到case命令吧,当在一组可能的值中寻找特定的值,可以写if-then-else语句,其中嵌套elif语句继续着if-then检测,这样子就很冗长麻烦。所以可以通过case命令简化,以列表导向格式检测单个变量的多个值,格式为:
case xxx in
xx | xx) command;
xx) command;
*) default command;
esac
下面通过一个例子:
[root@wzp ~]# cat test2
#!/bin/bash
case $USER in
root | testuser)
 echo welcome $USER
 echo your are admin;;
51cto)
 echo welcome $USER;;
*)
 echo welcome $USER;;
esac
[root@wzp ~]# ./test2
welcome root
your are admin
[root@wzp ~]# cp test2 /home/51cto/
cp:是否覆盖“/home/51cto/test2”? y
[root@wzp ~]# su - 51cto
[51cto@wzp ~]$ sh test2
welcome 51cto
判断是root和51cto用户则显示特定内容,如果通过其他用户执行,如下:
[root@wzp ~]# useradd testuser24
[root@wzp ~]# cp test2 /home/testuser24/
[root@wzp ~]# su - testuser24
[testuser24@wzp ~]$ sh test2
welcome testuser24
这个都比较好理解吧,假设通过存在的用户testuser执行,那显示的内容肯定是:
welcome testuser
your are admin
哈哈,说是admin也不正确了。

三、更多结构化命令
前面已经讲述了检查命令的输出和变量的值来操作shell脚本程序中的流。如下主要说明如何执行重复的过程和命令,使得一组命令循环下去,直到满足特定的条件。
知识内容:
# 使用for语句循环
# 使用until语句迭代
# 使用while语句
# 结合循环
# 重定向循环输出
1、for命令
重复一系列的命令是常见的编程实践,对于shell如处理目录下的所有文件、系统中的所有用户、或者文本文件中的所有行。对于bash shell的for循环命令格式:
for xxx in xx
do
  command
done
在xx参数里头提供一系列用户迭代的值,如下例:
1.1、读取列表中的值
[root@wzp ~]# cat test3
#!/bin/bash
for test in CCNA CCNP RHCE OCP OCM
do
  echo the learning list is $test
done
[root@wzp ~]# ./test3
the learning list is CCNA
the learning list is CCNP
the learning list is RHCE
the learning list is OCP
the learning list is OCM
上面的结果应该很好理解,有点还要说明的是迭代的最后一个值仍然有效,我可以通过在done后面重新显示出来,当然也可以再次修改它:
[root@wzp ~]# cat test3
#!/bin/bash
for test in CCNA CCNP RHCE OCP OCM
do
  echo the learning list is $test
done
echo the last list is $test
test='good job'
echo but the last point is $test
[root@wzp ~]# ./test3
the learning list is CCNA
the learning list is CCNP
the learning list is RHCE
the learning list is OCP
the learning list is OCM
the last list is OCM
but the last point is good job
可以看到最后的迭代值是OCM,重新赋值可以被修改的!
1.2、读取列表中的复杂值
如果列表中出现一些单引号,可以通过转义符号(反斜杠符号)或者双引号来定义单引号的值;还有就是如果要处理空格问题,可以通过双引号括起来,实现最终效果,如下例:
[root@wzp ~]# cat test3
#!/bin/bash
for cities in guang zhou shang hai bei jing
do
 echo my favorite city is $cities
done
[root@wzp ~]# ./test3
my favorite city is guang
my favorite city is zhou
my favorite city is shang
my favorite city is hai
my favorite city is bei
my favorite city is jing
很明显,这不是我要的结果,城市都被乱切了~
所以可以通过灵活使用双引号解决,如下:
[root@wzp ~]# cat test3
#!/bin/bash
for cities in "guang zhou" "shang hai" "bei jing"
do
 echo my favorite city is $cities
done
[root@wzp ~]# ./test3
my favorite city is guang zhou
my favorite city is shang hai
my favorite city is bei jing
1.3、读取命令中的值
生成in后面这个列表的值可以通过使用命令的输出,但这里需要协助`这个少见但是shell中常用的反引号!看下例子:
[root@wzp ~]# cat somefile
aa
bb
cc
dd
首先查看somefile这个文本文件内容
[root@wzp ~]# cat test3
#!/bin/bash
file=somefile
for words in `cat $file`
do
  echo the word is $words
done
[root@wzp ~]# ./test3
the word is aa
the word is bb
the word is cc
the word is dd
执行后可以理解到,列表时调用了somefile文件的每一行,注意somefile所处的位置是shell脚本中可以调用的,不然必须使用绝对路径或相对路径了。
这里我故意把somefile文件mv到别的地方去,结果执行文件是报错:
[root@wzp ~]# ./test3
cat: somefile: 没有那个文件或目录
1.4、使用通配符读取目录
文件通配是生产与指定通配符匹配的文件或者路径名的过程,如下例:
[root@wzp test]# pwd
/root/test
[root@wzp test]# touch aa bb cc
[root@wzp test]# mkdir dd ee ff
先创建三个文件格三个目录
[root@wzp ~]# cat test3
#!/bin/bash
for file in $HOME/test/*
do
 if [ -d "$file" ]
 then
    echo "$file is a directory"
 elif [ -f "$file" ]
 then
    echo "$file is a file"
 fi
done
[root@wzp ~]# ./test3
/root/test/aa is a file
/root/test/bb is a file
/root/test/cc is a file
/root/test/dd is a directory
/root/test/ee is a directory
/root/test/ff is a directory
通过if-then即可判断通配符*下的类型了~~
2、C语言式的for命令
这里顺便提及一下C语言式的for命令,在bash shell中也可执行。
不过C语言的for命令是通过指定变量为true值用于继续迭代的条件,当特定的条件为false时候就停止了循环,先看一个例子:
[root@wzp ~]# cat test3
#!/bin/bash
for (( i=1; i<=10; i++ ))
do
 echo the next num is $i
done
[root@wzp ~]# ./test3
the next num is 1
the next num is 2
the next num is 3
the next num is 4
the next num is 5
the next num is 6
the next num is 7
the next num is 8
the next num is 9
the next num is 10
通过判断变量是否为true执行循环,这里的双括号有点似曾相识,不说,你懂得!~~
3、while命令
while命令有点如if-then和for循环的结合,while通过定义要测试的命令,如果命令返回0则循环命令,如果返回非0则停止了命令集。其格式为:
while xxx
do
  other xx
done
先来看一个例子:
[root@wzp ~]# cat test3
#!/bin/bash
var=8
while [ $var -gt 0 ]
do
  echo $var
  var=$[ $var -1 ]
done
[root@wzp ~]# ./test3
8
7
6
5
4
3
2
1
当变量从8变到0不再大于0的时候则停止的循环
4、until命令
until命令刚好和while相反,until命令的测试命令退出状态非0,bash shell就执行列在循环中的命令,一旦测试条件返回0,就停止了循环,其格式:
until xxx
do
 other xx
done
如上看起来跟while很相似,其实用法也很相似的!
先看下面的例子:
[root@wzp ~]# cat test3
#!/bin/bash
var=24
until [ $var -eq 0 ]
do
  echo $var
  var=$[ $var -8 ]
done
[root@wzp ~]# ./test3
24
16
8
我拿24跟0比较是否相等,然后一直减去8,使得变量为0,则停止了循环。
5、嵌套循环
一条循环命令可以在循环中使用任何类型的命令,包括其他循环,比如在for循环中嵌套另一个for循环,或者在一个for循环中嵌套一个while循环或until循环。
下面举两个例子:
5.1、for循环嵌套到for循环
[root@wzp ~]# cat test3
#!/bin/bash
for (( a=1; a<=3; a++ ))
do
 echo outside loop is $a:
 for (( b=1; b<=3; b++ ))
 do
   echo inside loop is $b:
 done
done
[root@wzp ~]# ./test3
outside loop is 1:
inside loop is 1:
inside loop is 2:
inside loop is 3:
outside loop is 2:
inside loop is 1:
inside loop is 2:
inside loop is 3:
outside loop is 3:
inside loop is 1:
inside loop is 2:
inside loop is 3:
首先是外部循环,进入内部循环结束后又从外部循环重复一次,直到循环结束。
5.2、for循环嵌套到while循环
[root@wzp ~]# cat test3
#!/bin/bash
var1=5
while [ $var1 -ge 0 ]
do
  echo "outer loop is : $var1"
  for (( var2=1; var2<3; var2++ ))
  do
    echo "inner loop : $var2"
  done
  var1=$[ $var1-1 ]
done
[root@wzp ~]# ./test3
outer loop is : 5
inner loop : 1
inner loop : 2
outer loop is : 4
inner loop : 1
inner loop : 2
outer loop is : 3
inner loop : 1
inner loop : 2
outer loop is : 2
inner loop : 1
inner loop : 2
outer loop is : 1
inner loop : 1
inner loop : 2
outer loop is : 0
inner loop : 1
inner loop : 2
从上面可以看出嵌套循环的作用体现了,不是很难理解。
6、控制循环
如上定义了数据的各种循环方式,当我们需要某时刻退出循环的时候,如内部循环和外部循环,这个时候需要用到两个命令:
*break命令
*continue命令
如下先说说break命令控制的循环:
6.1、跳出单循环
通过break命令使得for循环和while循环、until循环可以中断跳出,先看个例子:
[root@wzp ~]# cat 6.1test
#!/bin/bash
for num in 1 2 3 4 5 6 7 8 9 10
do
  if [ $num -eq 5 ]
  then
     break
  fi
 echo "the num is : $num;"
done
echo 'the for loop is over !'
[root@wzp ~]# ./6.1test
the num is : 1;
the num is : 2;
the num is : 3;
the num is : 4;
the for loop is over !
通过for循环迭代列表中的1到10,当if-then满足条件等于5的时候就跳出循环,显示循环结束,不难理解。
同样的例子,对于while循环,如下:
[root@wzp ~]# cat 6.1test
#!/bin/bash
num=1
while [ $num -lt 10 ]
do
  if [ $num -eq 5 ]
  then
    break
  fi
  echo "the number is $num"
  num=$[ $num + 1 ]
done
echo 'the while loop is over !'
[root@wzp ~]# ./6.1test
the number is 1
the number is 2
the number is 3
the number is 4
the while loop is over !
同样是当if-then满足条件的时候跳出循环。
6.2、跳出内循环
有时候循环油嵌套,这个时候我们可以通过break来跳出循环中的内部循环,先来看个例子:
[root@wzp ~]# cat 6.2test
#!/bin/bash
for (( a=1; a<4; a++ ))
do
  echo "the outer loop is:$a"
  for (( b=1; b<100; b++ ))
  do
     if [ $b -eq 5 ]
     then
        break
     fi
     echo "inner loop is:$b"
 done
done 
[root@wzp ~]# ./6.2test
the outer loop is:1
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
the outer loop is:2
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
the outer loop is:3
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
从上面的例子看,先是外部循环,进入内部循环满足if-then则跳出去执行外部循环,直到外部循环结束,整个循环就结束了。
6.3、外部循环
既然有了上面的内部循环,势必可以想到有外部循环的存在,这里需要借助break命令的参数值,其格式很简单:
break n
其中n表示循环级别,默认情况是1
[root@wzp ~]# cat 6.3test
#!/bin/bash
for (( a=1; a<4; a++ ))
do
  echo "the outer loop is:$a"
  for (( b=1; b<100; b++ ))
  do
    if [ $b -gt 4 ]
    then
      break 2
    fi
    echo "inner loop is:$b"
  done
done
[root@wzp ~]# ./6.3test
the outer loop is:1
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
假如这里没有定义break等于2,那么结果应该如我们所想象的,如下:
[root@wzp ~]# ./6.3test
the outer loop is:1
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
the outer loop is:2
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
the outer loop is:3
inner loop is:1
inner loop is:2
inner loop is:3
inner loop is:4
这样子我们就没有实现结束外循环的目的了,通过指定break为2,当内循环满足if-then条件的时候就把外循环指定的循环级别给结束了
OK,说完了break来说下continue了~~~
6.4、continue命令
该命令是一种提前停止循环内命令,但不结束循环。先来看一个for循环中使用continue的示例:
[root@wzp ~]# cat 6.4test
#!/bin/bash
for (( a=1; a<24; a++ ))
do
  if [ $a -gt 8 ] && [ $a -lt 18 ]
  then
     continue
  fi
  echo "the num is:$a"
done
[root@wzp ~]# ./6.4test
the num is:1
the num is:2
the num is:3
the num is:4
the num is:5
the num is:6
the num is:7
the num is:8
the num is:18
the num is:19
the num is:20
the num is:21
the num is:22
the num is:23
如上表示说当满足a大于8并且小于18的时候就跳过该循环中余下的命令,但是循环继续进行。但满足if-then条件不成立的时候就继续循环下去。
7、处理循环的输出
在shell脚本中可以通过在done命令的末尾处添加处理命令实现,即为重定向>或者追加>>,看如下例子:
[root@wzp ~]# chmod u+x 7.1test
[root@wzp ~]# ./7.1test
[root@wzp ~]# cat 7.1test
#!/bin/bash
for (( a=1; a<10; a++ ))
do
  echo "the num is $a"
done > num.txt
[root@wzp ~]# cat num.txt
the num is 1
the num is 2
the num is 3
the num is 4
the num is 5
the num is 6
the num is 7
the num is 8
the num is 9
通过一个for循环,把指定的num内容重定向到num.txt文件中,该文件也就自动被创建在当前目录上了,这都好理解。