本章讨论bash shell的循环命令for、while和until

13.1 for命令

重复执行一系列命令在编程中很常见。

bash shell提供了for命令,允许你创建一个遍历一系列值的循环。每次迭代都使用其中一个值来执行已定义好的一组命令。下面是基本格式

for var in list

do

command

done

在list参数中需要提供迭代中要用到的一系列值。会依次迭代下去。每次迭代中,var会包含列表中要用到的一系列值。

do 和 done直接输入的命令可以是一条或多条标准的bash shell命令。

13.1.1 读取列表中的值

每次for命令遍历值列表,它都会将列表中的下一个值赋给$var变量。最后一次迭代后,$var变量的值会在shell脚本中剩余部分一直保持有效。(除非你修改了它)

13.1.2 读取列表中的复杂值

列表值的单引号是个大麻烦。

有两个方法可以解决

1)使用转义字符\。将单引号转义

2)使用双引号来定义用到单引号的值

在某个值两边使用双引号时,shell并不会将双引号当成值的一部分

13.1.3 从变量读取列表

将一系列的值都集中存储在了一个变量中,然后需要遍历变量中的整个列表。

#!/bin/bash

# for test

name1="xcx1 xcx2 xcx3"

name2=$name1" xcx4"

#for name in xc\'y "xcy'1 haha" xcy2 xcy3 xcy4 xcy5

for name in $name2

do

        echo "Hi, My name is $name"

done

echo "The last people is $name"

name2包含了用于迭代的标准文本值列表。注意。name2用了另一个复制语句向name2变量包含的以有列表中添(或者说拼接)加了一个值。

13.1.4 从命令读取值

生成列表中所需值的另外一个途径就是使用命令的输出。

可以用命令替换来执行任何能产生输出的命令,然后在for命令中使用该命令的输出。

例子:

新建一个文件states,内容如下:

再建一个test2

#!/bin/bash

file="states"

for state in $(cat $file)

do

        echo "Visit beautiful $state"

done

运行就好了。

for仍然以每次一行的方式遍历的cat命令输出的结果。

13.1.5 更改字段分隔符

1.特殊环境变量IFS:内部字段分割符。定义了bash shell用作字段分隔符的一系列字符。

2.默认情况下会将下列字符当做字段分隔符。1)空格 2)制表符 3)换行符

3. 如果bash shell 在数据中看到了这些字符中的任意一个,它就会假定这表明了列表中一个新数据字段的开始。

在处理包含空格的数据时会比较麻烦。所以需要修改IFS的值。

只识别换行符,就需要这么做:IFS=$’\n’。将这个语句假如脚本中,告诉bash shell在数据值中忽略空格和制表符。

#!/bin/bash

file="states"

IFS=$’\n’

for state in $(cat $file)

do

        echo "Visit beautiful $state"

done

还有一些绝妙用法:假如需要遍历一个文件中用冒号分割的值。就可以IFS=:

如果需要指定多个字符,只需要将它们在赋值行中串起来就行。IFS=$’\n’:;”  将换行符、冒号、分号、双引号作为字段分隔符

13.1.6 用通配符读取目录

可以用for命令来自动遍历目录中的文件。进行此操作时,必须在文件名或路径名中使用通配符。

它会强制使用文件扩展匹配(生成匹配指定通配符的文件名或路径名的过程)。

比如下面的例子:

  1 #!/bin/bash

  2 for file in /home/xcy/shell/*

  3 do

  4         if [ -d "$file" ]  # 加双引号为了解决文件名含有空格的问题

  5         then

  6                 echo "$file is directory"

  7         elif [ -f "$file" ] # 如果文件名有空格,没有双引号就会出错

  8         then

  9                 echo "$file is file"

 10         fi

 11 done

for语句首先使用了文件扩展匹配来遍历通配符生成的文件列表,然后会遍历列表中的下一个文件。可以将任意多的通配符放进列表中。

13.2 C语言风格的for命令

13.2.1 C语言的for命令

以下是bash中C语言风格的for循环的基本格式:

for (( variable assignment ; condition ; interation process ))

例子:

for (( a = 1; a < 10; a++ ))

(1)变量赋值可以有空格

(2)条件中的变量不以美元符开头

(3)迭代过程的算式没有用expr命令格式。

例子:

  1 #!/bin/bash

  2 # C for test

  3 for (( i = 10; i > 0; i-- ))

  4 do

  5         echo "For Test: i = $i"

  6 done

13.2.2 使用多个变量

C语言风格的for命令允许为迭代使用多个变量。循环会单独处理每个变量,可以为每个变量定义不同的迭代过程。

尽管可以使用多个变量,但你只能在for循环中定义一种条件。

例子:

  1 #!/bin/bash

  2 # C for multiple variables test

  3 for (( i=10, b = 0; i > 0; i--, b++ ))

  4 do

  5         echo "For Test: i = $i, b = $b"

  6 done

13.3 while命令

某种意义是if-then和for循环的混杂体。

while命令允许定义一个要测试的命令,然后循环执行一组命令,只要定义的测试命令返回的退出状态码0.它会在每次迭代的一开始测试test命令。在test命令返回非0退出状态码时,while会停止执行那组命令。test返回0,就接着迭代,否则暂停)

13.3.1 while的基本格式

while test command

do

  other commands

done

关键在于test command的退出状态码要随着循环中运行的命令而改变。否则就会停不下来

例子:用方括号检查循环命令中用的shell的变量的值

  1 #!/bin/bash

  2 i=10

  3 while [ $i -gt 5 ] # 相当于 >

  4 do

  5         echo "i = $i"

  6         i=$[ $i - 1 ]  # 不能用i--

  7 done

13.3.2 使用多个测试命令

可以在while后面接多个测试命令,只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环

例子:

1 #!/bin/bash

2 # multicommand test

3 var=2

4 while echo "var = $var"

5         [ $var -ge 0 ]  # -ge 相当于大于等于 >=

6 do

7         echo "this is inside the loop"

8         var=$[ $var - 1 ]

9 done

结果:

说明每次迭代中所有的命令都会执行,包括测试命令失败的最后一次迭代。

另外,如何指定多个测试命令。每个测试命令都出现再单独的一行上。

13.4 until命令

和while相反。until命令要求你指定一个通常返回非0退出状态码的测试命令。

只有测试命令退出状态码不为0,bash shell才会执行循环中列出的命令。

一旦返回了退出状态码0,循环就结束了。

格式:

until test commands

do

         other commands

done

例子:

  1 #!/bin/bash

  2 # until test

  3 var=100

  4 until [ $var -lt 0 ]  # 满足条件则结束,不满足则进循环

  5 # -eq  ==

  6 # -ge >=

  7 # -lt <

  8 do

  9         echo "until test: var = $var"

 10         var=$[ $var - 25 ]

 11 done

也可以执行多个测试命令,只在最后一个成立时停止。

13.5嵌套循环

循环语句可以在循环内使用任意类型的命令,包括其他循环命令。

注意在循环嵌套时执行次数是两次循环次数相乘。

例子:

  1 #!/bin/bash

  2 var1=3

  3 for (( var1=3; var1>0; var1-- ))

  4 do

  5         echo "for: var1 = $var1"

  6         var2=3

  7         while [ $var2 -gt 0 ]

  8         do

  9                 echo "  while: var2 = $var2"

 10                 var2=$[ $var2 - 1 ]

 11

 12                 var3=3

 13                 until [ $var3 -eq 0 ]

 14                 do

 15                         echo "    until: var3 = $var3"

 16                         var3=$[ $var3 - 1 ]

 17                 done

 18         done

 19 done

13.6循环处理文件数据

通常需要遍历存储在文件中的数据,需要结合两种技术:

1)使用嵌套循环

2)修改IFS环境变量

例子:

  1 #!/bin/bash

  2 # changing the IFS value

  3 IFS.OLD=$IFS

  4 IFS=$'\n'  # 分隔符变为换行符

  5 for entry in $(cat /etc/passwd)

  6 do

  7         echo "Values in [$entry]"

  8         IFS=:   # 分隔符变为冒号

  9         for value in $entry

 10         do

 11                 echo "   $value"

 12         done

 13 done

外循环解析一行一行的用户信息。内循环通过冒号分割,解析一个用户的具体信息。

13.7 控制循环

有两个命令可以控制循环内部的情况:

1)break   2)continue

13.7.1 break命令

退出循环的一种简单方法。可以退出任意类型的循环,包括while和until。

下面几种情况可以使用break命令。

1.跳出单个循环

执行break时,它会尝试跳出当前正在执行的循环。

  1 #!/bin/bash

  2 for var in 10 9 8 7 6 5 4 3 2 1

  3 do

  4         if [ $var -eq 5 ]

  5         then

  6                 echo "this is exec break;"

  7                 break

  8         fi

  9         echo "var = $var"

 10 done

这个方法也适用于while和until循环。

2.跳出内部循环

处理多个循环时,break会自动终止你所在的最内层的循环。

内层循环终止了,外层循环依然会继续执行。

3.跳出外部循环

有时你在内部循环,但需要停止外部循环。break命令接受单个命令行参数。

break n

n指定了要跳出的循环层级。默认情况下n为1.表示跳出当前循环。

若为2,就表示跳出上一级的外部循环。

例子:

1 #!/bin/bash

  2 for(( i=5; i>0; i-- ))

  3 do

  4         echo "outer loop: i = $i"

  5         for(( j = 0; j < 100; j++ ))

  6         do

  7                 echo "inside loop: j = $j"

  8                 if [ $j -eq 5 ]

  9                 then

 10                         break 2 # 跳出上一级循环

 11                 #       break # 跳出当前循环

 12                 fi

 13         done

 14 done

13.7.2 continue命令

提前终止某次循环中的命令,不会完全终止整个循环。

例子:

  1 #!/bin/bash

  2 for(( i=0; i < 10; i++ ))

  3 do

  4         if [ $i -gt 4 ] && [ $i -lt 8 ]

  5         then

  6                 continue

  7         fi

  8         echo "haha i = $i"

  9 done

注意:这个会跳过剩余的命令,如果在剩余的命令中要对测试条件变量进行改变就会出问题。这里需要留个心眼。

也可以通过命令行参数指定要继续执行哪一级循环。 continue n

  1 #!/bin/bash

  2 for(( i=0; i < 5; i++ ))

  3 do

  4         echo "out loop; i = $i"

  5         for(( j=0; j<4; j++ ))

  6         do

  7                 echo "          inside loop +++ j = $j"

  8                 if [ $j -eq 2 ]

  9                 then

 10                         continue 2  #继续上一级循环 还可以不接2,表示继续当前循环

 11                 fi

 12                 echo "          inside loop --- j = $j"

 13         done

 14 done

注意break和continue的区别:

break用于完全结束一个循环,后面的循环也不执行了。

continue用来结束当前循环,后面的循环还会执行。

比如:

for(i = 0; i < 10; i++)

do

         if [ $i –eq 5]

         then

                   break   # 6 , 7 , 8 ,9 就都不会打印了,结束了。

# continue # 仅仅不打印5

         fi

echo “i = $i”

done

13.8 处理循环的输出

直接上例子吧。直接在done后面接 > xxx.txt

13.9 实例

13.9.1 查找可执行文件

找出系统中有哪些可执行文件可供使用,只找PATH环境变量中所有的目录就行了

例子:

  1 #!/bin/bash

  2 # find files in the PATH

  3 IFS=:

  4 for folder in $PATH   # 将各个目录放入folder

  5 do

  6         echo "$folder"

  7         for file in $folder/*   # 迭代指定目录中的所有文件

  8         do

  9                 if [ -x $file ]  # 检查是否有可执行权限

 10                 then

 11                         echo "    $file"

 12                 fi

 13         done

 14 done

13.9.2 创建多个用户账户

让系统管理员更轻松。用脚本创建用户

1.先建立一个文本,里面放用户id和name。用逗号分隔

2. 再去读取上述文件中的信息

while IFS=',' read -r userid name

这个还是蛮有技巧的。read会自动读取读取.csv文本文件的下一行内容,不需要再写一个循环来处理。

read返回false时(就是读取完了)while就会退出,妙哉。

代码如下:

  1 #!/bin/bash

  2 # shell add user account

  3 input="users.csv"

  4 while IFS=',' read -r userid name # 读取里面的数据,IFS要设为逗号

  5 do

  6         echo "adding id:$userid  name:$name"

  7         useradd -c "$name" -m $userid

  8 done < "$input"

执行需要sudo权限。

13.9.2 再删除创建的用户

代码如下:

  1 #!/bin/bash

  2 # xcy test, del user

  3 IFS=$'\n'

  4 for user in $(cat /etc/passwd)

  5 do

  6 #       echo "$user"

  7         IFS=:

  8         for value in $user

  9         do

 10                 if [[ $value == xiaochongyong* ]]  # 这个*有点通配符的意思

 11                 then

 12                         echo "Userid: $value"

 13                         userdel $value

 14                 fi

 15                 break

 16         done

 17 done

注意那个break,因为/etc/passwd第一条就是userid,这里读取完userid就退出当前循环。

《Linux命令行与shell脚本编程大全》第十三章 更多的结构化命令的更多相关文章

  1. 《Linux命令行与shell脚本编程大全》23章24章

    第二十三章 使用其他shell bash shell是linux发行版中最广泛使用的shell.但是它并不是唯一的选择,还有其他的shell可以供你选择. 23.1 什么是dash shell 百度百 ...

  2. 《Linux命令行与shell脚本编程大全》第九章 安装软件程序

    包管理系统(PMS):用来进行软件安装.管理和删除的命令行工具 9.1包管理基础 1.主流的Linux发行版都采用了某种形式的包管理系统来控制软件和库的安装 2.PMS用一个数据库来记录:系统上安装了 ...

  3. Linux命令行与shell脚本编程大全.第3版(文字版) 超清文字-非扫描版 [免积分、免登录]

    此处免费下载,无需账号,无需登录,无需积分.收集自互联网,侵权通知删除. 点击下载:Linux命令行与shell脚本编程大全.第3版 (大小:约22M)

  4. 《Linux命令行与shell脚本编程大全 第3版》创建实用的脚本---11

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

  5. 《Linux命令行与shell脚本编程大全 第3版》高级Shell脚本编程---47

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

  6. 《Linux命令行与shell脚本编程大全 第3版》Shell脚本编程基础---57

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

  7. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---57

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

  8. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---56

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

  9. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---55

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

  10. 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---54

    以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:

随机推荐

  1. riot.js教程【二】组件撰写准则、预处理器、标签样式和装配方法

    基本要求 一个riot标签,就是展现和逻辑的组合(也就是html和JS): 以下是编写riot标签最基本的规则: 先撰写HTML,再撰写JS,JS代码可以写在<script>标签内部,但这 ...

  2. wpf C# 数据库 c/s 个人信息管理 wpf局域网通信

    系统功能基本要求 wpf局域网通信 WPF跨线程访问线程安全的数据如解决该类型的CollectionView不支持从调度程序线程以外的线程对其SourceCollection 读取信息null 读取发 ...

  3. JavaScript HTML Dom改变HTML

    本人为小白,首次写博客  有不正确的地方望多多指点与见谅!! 一,改变HTML的内容 语法: document.getElementById(id).innerHTML=new HTML: 具体用法: ...

  4. 简易RPC框架-客户端限流配置

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  5. apache+php+mysql运行环境

    建议Apache2.4+php5.6+mysql5.5+phpmyadmin4.4.4 参考: http://jingyan.baidu.com/article/fcb5aff797ec41edaa4 ...

  6. NIO FileChannel

    NIO提供了比传统的文件访问更好的访问方法,NIO有两个优化的方法:一个是 FIleChannel.transferTo FileChannel.transferFrom,另一个是FileChanne ...

  7. session失效问题

    具体设置很简单,方法有三种: ()在主页面或者公共页面中加入:session.setMaxInactiveInterval();参数600单位是秒,即在没有10分钟活动后,session将失效. 这里 ...

  8. VC++6.0在win8.1系统下运行失败的解决办法

    在win8.1系统下安装了VC++6,.0编译软件之后,发现打不开.出现下面的错误: 解决办法: 安装文件目录:Microsoft Visual Studio--common--MSDev98--Bi ...

  9. sklearn中各算法类的fit,fit_transform和transform函数

    在使用PCA和NFC中有三个函数fit,fit_transform,transform区分不清各自的功能.通过测试,勉强了解各自的不同,在这里做一些笔记. 1.fit_transform是fit和tr ...

  10. Visual Assist X 10.6.1830.0 常用快捷键

    Visual Assist X 10.6.1830.0 常用快捷键 1.Alt + G: 在定义与声明之间互跳. 2.Alt + O: 在.h与.cpp之间互跳.(O是字母O,不是数字零) 3.Alt ...