高级Bash脚本编程指南(15):循环

高级Bash脚本编程指南(15):循环

成于坚持,败于止步

循环就是迭代(重复)一些命令的代码块, 如果循环控制条件不满足的话, 就结束循环.

for循环

for arg in [list]

这是一个基本的循环结构. 它与C语言中的for循环结构有很大的不同.
for arg in [list]
do 
	command(s)... 
done
在循环的每次执行中, arg将顺序的访问list中列出的变量.

for arg in "$var1" "$var2" "$var3" ... "$varN" 
# 在第1次循环中, arg = $var1 
# 在第2次循环中, arg = $var2 
# 在第3次循环中, arg = $var3 
# 在第N此循环中, arg = $varN
# 在[list]中的参数加上双引号是为了阻止单词分割.
list中的参数允许包含通配符,如果do和for想在同一行中出现, 那么在它们之间需要添加一个分号.

for arg in [list] ; do

#!/bin/bash
for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto
do
	echo $planet
done
echo "---------------------------------------------------"
for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto"
do
	echo $planet
done
exit 0
结果:
root@ubuntu:~/resource/0510# ./test1 
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
Pluto
---------------------------------------------------
Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto
root@ubuntu:~/resource/0510# 
每个[list]中的元素都可能包含多个参数. 在处理参数组时, 这是非常有用的. 在这种情况下, 使用set命令来强制解析每个[list]中的元素, 并且将每个解析出来的部分都分配到一个位置参数中.
#!/bin/bash
for planet in "Mercury 36" "Venus 67" "Earth 93"  "Mars 142" "Jupiter 483"
do
	set -- $planet
	echo "$1		$2,000,000 miles from the sun"
done
exit 0
结果:
root@ubuntu:~/resource/0510# ./test1 
Mercury		36,000,000 miles from the sun
Venus		67,000,000 miles from the sun
Earth		93,000,000 miles from the sun
Mars		142,000,000 miles from the sun
Jupiter		483,000,000 miles from the sun
root@ubuntu:~/resource/0510# 

set -- $planet # 解析变量"planet"并且设置位置参数,不错的set --$planet O(∩_∩)O

再看一个更复杂的例子,说他复杂不是说循环复杂,而是有些方法也许你还没有接触过而已,没关系,跳过,以后这些都会是你的囊中之物,跑不掉,O(∩_∩)O~

#!/bin/bash

FILES="/usr/sbin/accept
/usr/sbin/pwck
/usr/sbin/chroot
/usr/bin/fakefile
/sbin/badblocks
/sbin/ypbind"     # 这是你所关心的文件列表.
                  # 扔进去一个假文件, /usr/bin/fakefile.

echo

for file in $FILES
do

  if [ ! -e "$file" ]       # 检查文件是否存在.
  then
    echo "$file does not exist."; echo
    continue                # 继续下一个.
  fi

  ls -l $file | awk '{ print $9 "         file size: " $5 }'  # 打印两个域.
  whatis `basename $file`   # 文件信息.
  # 注意whatis数据库需要提前建立好.
  # 要想达到这个目的, 以root身份运行/usr/bin/makewhatis.
  echo
done  

exit 0
执行结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test1.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh
root@ubuntu:~/resource/shell-study/0609-2013# ./test1.sh 

->         file size: 10
accept (2)           - accept a connection on a socket

         file size: 33012
pwck (8)             - verify integrity of password files

         file size: 30224
chroot (8)           - run command or interactive shell with special root directory
chroot (2)           - change root directory

/usr/bin/fakefile does not exist.

         file size: 22072
badblocks (8)        - search a device for bad blocks

/sbin/ypbind does not exist.

root@ubuntu:~/resource/shell-study/0609-2013# 

遗留问题:awk命令

再来一个例子,这个号理解一点:

#!/bin/bash

for file in *
#           ^  在表达式中识别文件名匹配时,
#+             Bash将执行文件名扩展.
do
  ls -l "$file"  # 列出在$PWD(当前目录)中的所有文件.
  #  回想一下,通配符"*"能够匹配所有文件,
  #+ 然而,在"globbing"中,是不能比配"."文件的.

  #  如果没匹配到任何文件,那它将扩展成自己.
  #  为了不让这种情况发生,那就设置nullglob选项
  #+   (shopt -s nullglob).
  #  感谢, S.C.
done
echo; echo
for file in [jx]*
do
  rm -f $file    # 只删除当前目录下以"j"或"x"开头的文件.
  echo "Removed file \"$file\"".
done

echo
exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh  test2.sh
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test2.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh  test2.sh
root@ubuntu:~/resource/shell-study/0609-2013# touch jrm
root@ubuntu:~/resource/shell-study/0609-2013# touch xrm
root@ubuntu:~/resource/shell-study/0609-2013# ls
jrm  test1.sh  test2.sh  xrm
root@ubuntu:~/resource/shell-study/0609-2013# ./test2.sh 
-rw-r--r-- 1 root root 0 2013-06-09 03:27 jrm
-rwxr-xr-x 1 root root 673 2013-06-09 03:18 test1.sh
-rwxr-xr-x 1 root root 643 2013-06-09 03:26 test2.sh
-rw-r--r-- 1 root root 0 2013-06-09 03:27 xrm


Removed file "jrm".
Removed file "xrm".

root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh  test2.sh
root@ubuntu:~/resource/shell-study/0609-2013# 

可以看到了,为了更直观的测试结果,我新建了两个文件jrm和xrm,最后都成功被删除掉了,注意这里是j或x开头的文件,遗留问题:为什么时“或”呢

在for循环中省略in [list]部分

上面我们在使用for循环时,使用的方法都是for args in [list] 这种方法,其实你是可以偷下懒的,省掉后面的in [list],不过作为一个有理想的初学者,不建议大家这样做,O(∩_∩)O~,看看例子,知道就好

#!/bin/bash

#  使用两种方式来调用这个脚本, 一种带参数, 另一种不带参数,
#+ 并观察在这两种情况下, 此脚本的行为.

for a
do
echo -n "$a "
done

#  省略'in list'部分, 因此循环将会操作'$@'
#+ (包括空白的命令行参数列表).

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test3.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh  test2.sh  test3.sh
root@ubuntu:~/resource/shell-study/0609-2013# ./test3.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ./test3.sh hello
hello root@ubuntu:~/resource/shell-study/0609-2013# 
没带参数时没有任何输出,说明list中是没有参数的,让后加入“hello”参数,输出了hello,哦,为什么没有回车,仔细看上面的代码,echo后面加上了“-n”参数,那肯定不回车啦,说明默认的这里没有in [list],list就是输入参数

上面的list都是直接给出,当然也可以直接把一个返回值作为list,其实没什么大不了的,一个意思,看实例

#!/bin/bash
# [list]是由命令替换所产生的.
NUMBERS="9 7 3 8 37.53"

for number in `echo $NUMBERS`  # for number in 9 7 3 8 37.53
do
  echo -n "$number "
done

echo 
exit 0
结果:
hello root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test4.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ./test4.sh 
9 7 3 8 37.53 
root@ubuntu:~/resource/shell-study/0609-2013# 
留意一下``吧,别的没什么

再来个复杂一点的例子,通过这些例子,慢慢你会很好的掌握shell,这也是我选择这本书的原因,实践是检验真理的唯一标准,O(∩_∩)O~

#!/bin/bash

# 对于二进制文件的"grep"替换. 
# 与"grep -a"的效果相似

E_BADARGS=65
E_NOFILE=66

if [ $# -ne 2 ]
then
  echo "Usage: `basename $0` search_string filename"
  exit $E_BADARGS
fi

if [ ! -f "$2" ]
then
  echo "File \"$2\" does not exist."
  exit $E_NOFILE
fi  


IFS=$'\012'       # 由Paulo Marcel Coelho Aragao提出的建议.
                  # 也就是:  IFS="\n"
for word in $( strings "$2" | grep "$1" )
# "strings" 命令列出二进制文件中的所有字符串.
# 输出到管道交给"grep",然后由grep命令来过滤字符串.
do
  echo $word
done

# S.C. 指出, 行23 - 29 可以被下边的这行来代替,
#    strings "$2" | grep "$1" | tr -s "$IFS" '[\n*]'


# 试试用"./bin-grep.sh mem /bin/ls"来运行这个脚本.
exit 0
看看结果:

root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test5.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh  test2.sh  test3.sh  test4.sh  test5.sh
root@ubuntu:~/resource/shell-study/0609-2013# ./test5.sh 
Usage: test5.sh search_string filename
root@ubuntu:~/resource/shell-study/0609-2013# ./test5.sh test3.sh 
Usage: test5.sh search_string filename
root@ubuntu:~/resource/shell-study/0609-2013# ./test5.sh mem /bin/ls
__mempcpy_chk
memset
mempcpy
memcpy
__memcpy_chk
sigismember
memmove
memory exhausted
root@ubuntu:~/resource/shell-study/0609-2013# ./test5.sh ech test4.sh 
for number in `echo $NUMBERS`  # for number in 9 7 3 8 37.53
  echo -n "$number "
echo 
root@ubuntu:~/resource/shell-study/0609-2013# 
这个程序中中使用了管道,grep,还有IFS,这些将慢慢跟你熟悉起来

接着看一个程序,实现功能:列出系统上的所有用户

#!/bin/bash
# userlist.sh

PASSWORD_FILE=/etc/passwd
n=1           # User number

for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE" )
# 域分隔 = :             ^^^^^^
# 打印出第一个域                 ^^^^^^^^
# 从password文件中取得输入                     ^^^^^^^^^^^^^^^^^
do
  echo "USER #$n = $name"
  let "n += 1"
done

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x userlist.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ls
test1.sh  test2.sh  test3.sh  test4.sh  test5.sh  userlist.sh
root@ubuntu:~/resource/shell-study/0609-2013# ./userlist.sh 
USER #1 = root
USER #2 = daemon
USER #3 = bin
USER #4 = sys
USER #5 = sync
USER #6 = games
USER #7 = man
USER #8 = lp
USER #9 = mail
USER #10 = news
USER #11 = uucp
USER #12 = proxy
USER #13 = www-data
USER #14 = backup
USER #15 = list
USER #16 = irc
USER #17 = gnats
USER #18 = nobody
USER #19 = libuuid
USER #20 = syslog
USER #21 = messagebus
USER #22 = avahi-autoipd
USER #23 = avahi
USER #24 = couchdb
USER #25 = speech-dispatcher
USER #26 = usbmux
USER #27 = haldaemon
USER #28 = kernoops
USER #29 = pulse
USER #30 = rtkit
USER #31 = saned
USER #32 = hplip
USER #33 = gdm
USER #34 = hai-tao
USER #35 = statd
root@ubuntu:~/resource/shell-study/0609-2013# 
又是awk,不行,本想后面再说这个命令的,但他老是蹦出来阻碍大家的理解,这里暂时说一点吧,知道一点也好,上面awk的输入是/etc/passwd,我们就先把passwd中的内容列出来,看看awk到底是怎么来出来数据的

root@ubuntu:~/resource/shell-study/0609-2013# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
messagebus:x:102:107::/var/run/dbus:/bin/false
avahi-autoipd:x:103:110:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false
avahi:x:104:111:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false
couchdb:x:105:113:CouchDB Administrator,,,:/var/lib/couchdb:/bin/bash
speech-dispatcher:x:106:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/sh
usbmux:x:107:46:usbmux daemon,,,:/home/usbmux:/bin/false
haldaemon:x:108:114:Hardware abstraction layer,,,:/var/run/hald:/bin/false
kernoops:x:109:65534:Kernel Oops Tracking Daemon,,,:/:/bin/false
pulse:x:110:115:PulseAudio daemon,,,:/var/run/pulse:/bin/false
rtkit:x:111:117:RealtimeKit,,,:/proc:/bin/false
saned:x:112:118::/home/saned:/bin/false
hplip:x:113:7:HPLIP system user,,,:/var/run/hplip:/bin/false
gdm:x:114:120:Gnome Display Manager:/var/lib/gdm:/bin/false
hai-tao:x:1000:1000:ubuntu 10.04,,,:/home/hai-tao:/bin/bash
statd:x:115:65534::/var/lib/nfs:/bin/false
root@ubuntu:~/resource/shell-study/0609-2013# 
一说就明白了,上面awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE"实现:将上面显示的内容以“:”作为分隔符对每行进行拆分,拆分出来的结果依次可以用$1,$2,$3,......取出,所以上面只是把每行的第一个“:”前的内容打印出来,就是我们想要的结果,本机上的所有用户了,还算简单??当然,这是awk最简单的部分了,O(∩_∩)O~

接着上菜,实现功能:在目录的所有文件中查找源字串

#!/bin/bash
# findstring.sh:
# 在一个指定目录的所有文件中查找一个特定的字符串.

directory=/usr/bin/
fstring="Free Software Foundation"  # 查看哪个文件中包含FSF.

for file in $( find $directory -type f -name '*' | sort )
do
  strings -f $file | grep "$fstring" | sed -e "s%$directory%%"
  #  在"sed"表达式中,
  #+ 我们必须替换掉正常的替换分隔符"/",
  #+ 因为"/"碰巧是我们需要过滤的字符串之一.
  #  如果不用"%"代替"/"作为分隔符,
  #  那么这个操作将失败,并给出一个错误消息.(试一试).
done

exit 0
结果:

root@ubuntu:~/resource/shell-study/0609-2013# chmod +x findstring.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ls
findstring.sh  test1.sh  test2.sh  test3.sh  test4.sh  test5.sh  userlist.sh
root@ubuntu:~/resource/shell-study/0609-2013# ./findstring.sh 
[: Copyright %s %d Free Software Foundation, Inc.
addr2line: Copyright 2009 Free Software Foundation, Inc.
alacarte: #   License as published by the Free Software Foundation; either
amuFormat.sh: # the Free Software Foundation, either version 3 of the License, or
apport-bug: # Free Software Foundation; either version 2 of the License, or (at your
apport-cli: # Free Software Foundation; either version 2 of the License, or (at your
apport-unpack: # Free Software Foundation; either version 2 of the License, or (at your
aptdcon: # the Free Software Foundation; either version 2 of the License, or
aptdcon: # with this program; if not, write to the Free Software Foundation, Inc.,
apturl-gtk: # by the Free Software Foundation; either version 2 of the License, or (at
ar: Copyright 2009 Free Software Foundation, Inc.
......
......
简单说一下,其中strings -f $file负责读出file中的内容,并在内容的每一行前面加上一句话,比如文件名是hello,则文件中的每行前会加上“hello:”打印总结果,这里把这个结果作为grep "$fstring"的输入,所以这里会在刚才获得的内容中查找这个定义好的字符串,以后再慢慢琢磨shell中的这些命令吧,这个时候去钻这些命令可能会吃力一点

遗留问题:strings -f 命令,sed -e命令

接着看例子,列出目录中所有的符号链接

#!/bin/bash
# symlinks.sh: 列出目录中所有的符号链接文件.


directory=${1-`pwd`}
#  如果没有其他特殊的指定,
#+ 默认为当前工作目录.
#  下边的代码块, 和上边这句等价.
# ----------------------------------------------------------
# ARGS=1                 # 需要一个命令行参数.
#
# if [ $# -ne "$ARGS" ]  # 如果不是单个参数的话...
# then
#   directory=`pwd`      # 当前工作目录
# else
#   directory=$1
# fi
# ----------------------------------------------------------

echo "symbolic links in directory \"$directory\""

for file in "$( find $directory -type l )"   # -type l = 符号链接
do
  echo "$file"
done | sort    # 否则的话, 列出的文件都是未经排序的.
#  严格意义上说, 这里并不一定非要一个循环不可.
#+ 因为"find"命令的输出将被扩展成一个单词.
#  然而, 这种方式很容易理解也很容易说明.

#  就像Dominik 'Aeneas' Schnitzer所指出的,
#+ 如果没将$( find $directory -type l )用""引用起来的话,
#+ 那么将会把一个带有空白部分的文件名拆分成
#  以空白分隔的两部分(文件名允许有空白).
#  即使这里只会取出每个参数的第一个域.

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# ./symlinks.sh 
symbolic links in directory "/root/resource/shell-study/0609-2013"

root@ubuntu:~/resource/shell-study/0609-2013# 
我的这个目录下没有链接文件,有兴趣的可以测试一下。

还有一种很独特的很想c语言风格的for循环,也许你会更有兴趣看看

#!/bin/bash
# 两种循环到10的方法.

echo

# 标准语法.
for a in 1 2 3 4 5 6 7 8 9 10
do
  echo -n "$a "
done

echo; echo

# +==========================================+

# 现在, 让我们用C风格语法来做相同的事情.

LIMIT=10

for ((a=1; a <= LIMIT ; a++))  # 双圆括号, 并且"LIMIT"变量前面没有"$".
do
  echo -n "$a "
done                           # 这是一个借用'ksh93'的结构.

echo; echo

# +=========================================================================+

# 让我们使用C语言的"逗号操作符", 来同时增加两个变量的值.

for ((a=1, b=1; a <= LIMIT ; a++, b++))  # 逗号将同时进行两条操作.
do
  echo -n "$a-$b "
done

echo; echo

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test6.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ./test6.sh 

1 2 3 4 5 6 7 8 9 10 

1 2 3 4 5 6 7 8 9 10 

1-1 2-2 3-3 4-4 5-5 6-6 7-7 8-8 9-9 10-10 

root@ubuntu:~/resource/shell-study/0609-2013# 
是不是感觉犀利了好多,O(∩_∩)O~

还有一个实例,我没有去试验,这里放在这里,有兴趣可以试一下,我就偷懒了,O(∩_∩)O~

#!/bin/bash
# Faxing (前提是'fax'必须已经安装好).

EXPECTED_ARGS=2
E_BADARGS=65

if [ $# -ne $EXPECTED_ARGS ]
# 检查命令行参数的个数是否正确.
then
   echo "Usage: `basename $0` phone# text-file"
   exit $E_BADARGS
fi


if [ ! -f "$2" ]
then
  echo "File $2 is not a text file"
  exit $E_BADARGS
fi


fax make $2              # 从纯文本文件中创建传真格式的文件.

for file in $(ls $2.0*)  # 连接转换过的文件.
                         # 在变量列表中使用通配符.
do
  fil="$fil $file"
done

efax -d /dev/ttyS3 -o1 -t "T$1" $fil   # 干活的地方.


# S.C. 指出, 通过下边的命令可以省去for循环.
#    efax -d /dev/ttyS3 -o1 -t "T$1" $2.0*
# 但这并不十分具有讲解意义[嘿嘿].

exit 0

while

这种结构在循环的开头判断条件是否满足, 如果条件一直满足, 那么就一直循环下去 (返回0作为退出状态码). 与for循环的区别是, while循环更适合在循环次数未知的情况下使用.

while [condition]
do 
	command... 
done
与for循环一样, 如果想把do和条件判断放到同一行上的话, 还是需要一个分号.

while [condition] ; do

需要注意一下某种特定的while循环, 比如getopts结构, 好像和这里所介绍的模版有点脱节,以后遇到再说吧

看一个while的实例
#!/bin/bash

var0=0
LIMIT=10

while [ "$var0" -lt "$LIMIT" ]
do
  echo -n "$var0 "        # -n 将会阻止产生新行.
  #             ^           空格, 数字之间的分隔.

  var0=`expr $var0 + 1`   # var0=$(($var0+1))  也可以.
                          # var0=$((var0 + 1)) 也可以.
                          # let "var0 += 1"    也可以.
done                      # 使用其他的方法也行.

echo

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# ./test8.sh 
0 1 2 3 4 5 6 7 8 9 
root@ubuntu:~/resource/shell-study/0609-2013# 

另一个while循环实例:

#!/bin/bash

echo
                               # 等价于:
while [ "$var1" != "end" ]     # while test "$var1" != "end"
do
  echo "Input variable #1 (end to exit) "
  read var1                    # 为什么不使用'read $var1'?
  echo "variable #1 = $var1"   # 因为包含"#", 所以需要""
  # 如果输入为'end', 那么就在这里echo.
  # 不在这里判断结束, 在循环顶判断.
  echo
done

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test9.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ./test9.sh 

Input variable #1 (end to exit) 
hello
variable #1 = hello

Input variable #1 (end to exit) 
yes
variable #1 = yes

Input variable #1 (end to exit) 
end
variable #1 = end

root@ubuntu:~/resource/shell-study/0609-2013# 

多条件的while循环实例:

#!/bin/bash

var1=unset
previous=$var1

while echo "previous-variable = $previous"
      echo
      previous=$var1
      [ "$var1" != end ] # 纪录之前的$var1.
      # 这个"while"中有4个条件, 但是只有最后一个能够控制循环.
      # *最后*的退出状态就是由这最后一个条件来决定.
do
echo "Input variable #1 (end to exit) "
  read var1
  echo "variable #1 = $var1"
done

# 尝试理解这个脚本的运行过程.
# 这里还是有点小技巧的.

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test10.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ./test10.sh 
previous-variable = unset

Input variable #1 (end to exit) 
hello
variable #1 = hello
previous-variable = unset

Input variable #1 (end to exit) 
yes
variable #1 = yes
previous-variable = hello

Input variable #1 (end to exit) 
end
variable #1 = end
previous-variable = yes

root@ubuntu:~/resource/shell-study/0609-2013# 
结果和上面是一样的, 很重要的是最后一条判断语句的退出状态决定了while最终判断条件是真还是假,这个相当重要

与for循环一样, while循环也可以通过(())来使用C风格的语法,来看一个C风格的while循环:

#!/bin/bash
# wh-loopc.sh: 循环10次的"while"循环.

LIMIT=10
a=1

while [ "$a" -le $LIMIT ]
do
  echo -n "$a "
  let "a+=1"
done           # 到目前为止都没有什么令人惊奇的地方.

echo; echo

# +=================================================================+

# 现在, 重复C风格的语法.

((a = 1))      # a=1
# 双圆括号允许赋值两边的空格, 就像C语言一样.

while (( a <= LIMIT ))   # 双圆括号, 变量前边没有"$".
do
  echo -n "$a "
  ((a += 1))   # let "a+=1"
  # Yes, 看到了吧.
  # 双圆括号允许像C风格的语法一样增加变量的值.
done

echo

# 现在, C程序员可以在Bash中找到回家的感觉了吧.

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# ./test11.sh 
1 2 3 4 5 6 7 8 9 10 

1 2 3 4 5 6 7 8 9 10 
root@ubuntu:~/resource/shell-study/0609-2013# 

until

这个结构在循环的顶部判断条件, 并且如果条件一直为false, 那么就一直循环下去. (与while循环相反),c语言里没有这样的,不过这是shell,O(∩_∩)O~

until [condition-is-true]
do 
nbsp;command... 
done
注意, until循环的条件判断在循环的顶部, 这与某些编程语言是不同的.

与for循环一样, 如果想把do和条件判断放在同一行里, 那么就需要使用分号.

until [condition-is-true] ; do

看一个实例:

#!/bin/bash

END_CONDITION=end

until [ "$var1" = "$END_CONDITION" ]
# 在循环的顶部进行条件判断.
do
  echo "Input variable #1 "
  echo "($END_CONDITION to exit)"
  read var1
  echo "variable #1 = $var1"
  echo
done

exit 0
结果:
root@ubuntu:~/resource/shell-study/0609-2013# chmod +x test12.sh 
root@ubuntu:~/resource/shell-study/0609-2013# ./test12.sh 
Input variable #1 
(end to exit)
hello
variable #1 = hello

Input variable #1 
(end to exit)
yes
variable #1 = yes

Input variable #1 
(end to exit)
end
variable #1 = end

root@ubuntu:~/resource/shell-study/0609-2013# 
终于结果了这一章,不容易啊!累!码字不容易啊!O(∩_∩)O~

先到这里了,O(∩_∩)O~

我的专栏地址:http://blog.csdn.net/column/details/shell-daily-study.html

待续。。。。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值