《Linux Command Line and Shell Scripting Bible》Part 13 更多的结构化命令(for while until)
for命令
基本语法格式
for var in list
do
commands
done
也可以写成
for var in list; do
commands
done
读取列表中的值
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test1 The next state Alabama The next state Alaska The next state Arizona The next state Arkansas The next state California The next state Colorado shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test1 #! /bin/bash for test in Alabama Alaska Arizona Arkansas California Colorado do echo The next state $test done
for循环结束,变量会保持最后一次的值,这个跟Python一样
读取列表中的复杂值
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./badtest1 word:I word:dont know if thisll word:work shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat badtest1 #! /bin/bash for test in I don't know if this'll work do echo "word:$test" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
shell看到列表值中的单引号并尝试使用它们来定义一个单独的数据值。这可能不是你想要的
可以通过转义符号或者双引号解决
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test2 word:I word:don't word:know word:if word:this'll word:work shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test2 #! /bin/bash for test in I don\'t know if "this'll" work do echo "word:$test" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
从前面可以看出for循环中用空格分割,如果想显示有空格的数据,也可以通过双引号帮助
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test3 Now going to Nevada Now going to New Hampshire Now going to New Mexico Now going to New Tork shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test3 #! /bin/bash for test in Nevada "New Hampshire" "New Mexico" "New Tork" do echo "Now going to $test" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
从变量读取列表
将一系列值都集中存储在一个变量中,然后需要遍历变量中的整个列表[这里我犯了一个初级的错误,变量赋值的过程中如果不带引号,是不对的。系统把自动把空格当成分割]
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test4 Alabama Alaska Arizona Arkansas Colorado Have you ever visited Alabama Have you ever visited Alaska Have you ever visited Arizona Have you ever visited Arkansas Have you ever visited Colorado Have you ever visited Connecticut shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test4 #! /bin/bash list="Alabama Alaska Arizona Arkansas Colorado" echo $list list=$list" Connecticut" for state in $list do echo "Have you ever visited $state" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
从一个字符串中读取,遇到空格自动分割,还有一个字符串的合并不用任何符号,两个并列放一起就好
从命令读取值
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test5 Visit baeutiful Alabama Visit baeutiful ALska Visit baeutiful Colorado Visit baeutiful Commectiuct shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test5 #! /bin/bash file="states" for state in $(cat $file) do echo "Visit baeutiful $state" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat states Alabama ALska Colorado Commectiuct shijianzhongdeMacBook-Pro:part_13 shijianzhong$
上面的字段,如果名字中有空格,还是会自动分割
更改字段分隔符
IFS 内部字段分隔符(internal field separator)
IFS环境变量定义了bash shell用作字段分割符的一系列字符。默认情况下为:空格,制表符, 换行符
如果要修改可以重新定义IFS=$'\n',这样IFS只能换行符了
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test5b Visit baeutiful Alabama ok Visit baeutiful ALska Visit baeutiful Colorado Visit baeutiful Commectiuct shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test5b #! /bin/bash file="states" IFS=$'\n' for state in $(cat $file) do echo "Visit baeutiful $state" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
如果要中间临时修改以下IFS,后面重新恢复原来的,可以中间使用一个临时变量
IFS.OLD=$IFS IFS=$'\n' #运行需要设定IFS的取悦 IFS=$IFS.OLD
如果要指定多个IFS分隔符,只要将它们赋值行穿起来就可以(注意,测试过了,只能用符号当分隔符,不用用字母)
IFS=$'\n':'
在mac上测试单引号与双引号没法设置成分隔符
用通配符读取目录
必须在文件名或路径名中使用通配符
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test6 ./badtest1 is a file ./states is a file ./test is a directory ./test1 is a file ./test2 is a file ./test3 is a file ./test4 is a file ./test5 is a file ./test5b is a file ./test6 is a file shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test6 #! /bin/bash for file in ./* do if [ -d "$file" ] # 判断是否是文件夹,加上双引号比较号,要不然碰到有空格的文件名,就会出现参数错误 then echo "$file is a directory" elif [ -f $file ] # 判断是否是文件 then echo $file is a file fi done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
也可以使用多个目录通配符,并且合并在同一个for语句下
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test7 ./badtest1 is a file ./states is a file ./test is a directory ./test1 is a file ./test2 is a file ./test3 is a file ./test4 is a file ./test5 is a file ./test5b is a file ./test6 is a file ./test7 is a file /no_way/heloo doesn't exist shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test7 #! /bin/bash for file in ./* /no_way/heloo # 匹配两个路径,中间用空格 do if [ -d "$file" ] # 判断是否是文件夹,加上双引号比较号,要不然碰到有空格的文件名,就会出现参数错误 then echo "$file is a directory" elif [ -f $file ] # 判断是否是文件 then echo $file is a file else echo "$file doesn't exist" fi done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
C语言风格的for命令
for (( variable ; condition; iteration process))
for (( a = 1; a < 10; a++))
这样的for循环有如下特点,变量赋值可以有空格,条件表达式中的变量可以不以$符开头,迭代过程的算式未用expr命令格式
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test8 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 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test8 #! /bin/bash for (( i=1; i<=10; i++)) do echo "The next num is $i" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
C语言风格的for命令也允许为迭代使用多个变量。循环会单独处理每个变量,你可以为每个变量定义不通的迭代过程。可以使用多个变量,但你只能在for循环中定义一种条件
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test9 1 - 5 2 - 4 3 - 3 4 - 2 5 - 1 6 - 0 7 - -1 8 - -2 9 - -3 10 - -4 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test9 #! /bin/bash for (( a=1, b=5; a <=10; a++, b-- )) do echo "$a - $b" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
while 命令
while命令的格式是
while test command
do
other commands
done
test command 一般用[ ]来检测里面的值
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test10 10 9 8 7 6 5 4 3 2 1 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test10 #! /bin/bash var1=10 while [ $var1 -gt 0 ] # 大于0的情况 do echo $var1 var1=$[ $var1 - 1 ] done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
使用多个测试命令
while命令允许你在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环。是最后一个状态
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test11 10 This is inside the loop 9 This is inside the loop 8 This is inside the loop 7 This is inside the loop 6 This is inside the loop 5 This is inside the loop 4 This is inside the loop 3 This is inside the loop 2 This is inside the loop 1 This is inside the loop 0 This is inside the loop -1 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test11 #! /bin/bash var1=10 while echo $var1 # 条件1 [ $var1 -ge 0 ] # 条件2 do echo "This is inside the loop" var1=$[ $var1 - 1 ] done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
13.4 until命令
until test commands
do
other commands
done
跟while一样,只不过这个是在条件不成立的时候执行,也就是test commands退出值不是0的时候执行,使用方式跟while基本一样
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test12 100 75 50 25 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test12 #! /bin/bash var1=100 until [ $var1 -eq 0 ] do echo $var1 var1=`expr $var1 - 25` done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
同样使用多个测试命令
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test13 100 Inside the loop: 100 75 Inside the loop: 75 50 Inside the loop: 50 25 Inside the loop: 25 0 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test13 #! /bin/bash var1=100 until echo $var1 [ $var1 -eq 0 ] do echo Inside the loop: $var1 var1=`expr $var1 - 25` done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
13.5嵌套循环
对于学过其他语言的,非常容易理解
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test14 Starting loop 1: Inside loop: 1 Inside loop: 2 Inside loop: 3 Starting loop 2: Inside loop: 1 Inside loop: 2 Inside loop: 3 Starting loop 3: Inside loop: 1 Inside loop: 2 Inside loop: 3 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test14 #! /bin/bash for (( a = 1; a <= 3; a++ )) do echo "Starting loop $a:" for (( b = 1; b <= 3; b++ )) # 内层的循环 do echo " Inside loop: $b" done done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
双层循环,while循环内套for循环
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test15 Outer loop: 5 Inner loop: 5 * 1 = 5 Inner loop: 5 * 2 = 10 Outer loop: 4 Inner loop: 4 * 1 = 4 Inner loop: 4 * 2 = 8 Outer loop: 3 Inner loop: 3 * 1 = 3 Inner loop: 3 * 2 = 6 Outer loop: 2 Inner loop: 2 * 1 = 2 Inner loop: 2 * 2 = 4 Outer loop: 1 Inner loop: 1 * 1 = 1 Inner loop: 1 * 2 = 2 Outer loop: 0 Inner loop: 0 * 1 = 0 Inner loop: 0 * 2 = 0 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat ./test15 #! /bin/bash var1=5 while [ $var1 -ge 0 ] do echo "Outer loop: $var1" for (( var2 = 1; var2 < 3; var2++ )) do var3=$[ $var1 * $var2 ] echo " Inner loop: $var1 * $var2 = $var3" done var1=`expr $var1 - 1` done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
最后抄写书中一个until与while混用的循环
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test16 Outer loop: 3 Inner loop: 3/ 1 = 3.0000 Inner loop: 3/ 2 = 1.5000 Inner loop: 3/ 3 = 1.0000 Inner loop: 3/ 4 = .7500 Outer loop: 2 Inner loop: 2/ 1 = 2.0000 Inner loop: 2/ 2 = 1.0000 Inner loop: 2/ 3 = .6666 Inner loop: 2/ 4 = .5000 Outer loop: 1 Inner loop: 1/ 1 = 1.0000 Inner loop: 1/ 2 = .5000 Inner loop: 1/ 3 = .3333 Inner loop: 1/ 4 = .2500 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test16 #! /bin/bash var1=3 until [ $var1 -eq 0 ] # 只要不等于0就执行 do echo "Outer loop: $var1" var2=1 while [ $var2 -lt 5 ] # 小于5执行 do var3=$(echo "scale=4; $var1 / $var2" | bc) echo " Inner loop: $var1/ $var2 = $var3" var2=$[ $var2 + 1 ] # 执行内部逻辑 done var1=$[ $var1 - 1 ] # 外部的逻辑执行 done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
话说里面的小数乘除法我又忘记了
循环处理文件数据
就是通过修改IFS的值来设定分隔符
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat ifs.sh #! /bin/bash IFS.OLD=$IFS IFS=$'\n' for entry in `cat /etc/passwd` do echo "Values in $entry -" IFS=: for value in $entry do echo " ${value}" done done
我的参数里面有*到时候输出的时候会出现问题,暂时没有好的办法搞定
控制循环
break与continue命令,这个用法跟Python也差不多
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test17 Iteration number: 1 Iteration number: 2 Iteration number: 3 Iteration number: 4 The for loop is completed shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test17 #! /bin/bash for var1 in {1..10} do if [ $var1 -eq 5 ] then break fi echo "Iteration number: $var1" done echo "The for loop is completed" shijianzhongdeMacBook-Pro:part_13 shijianzhong$
break 同样支持while和until,不写了,用法一模一样
跳出内部循环,单个break会跳出单个循环
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ chmod u+x test19 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test19 Outer loop: 1 Inner loop: 1 Inner loop: 2 Inner loop: 3 Inner loop: 4 Outer loop: 2 Inner loop: 1 Inner loop: 2 Inner loop: 3 Inner loop: 4 Outer loop: 3 Inner loop: 1 Inner loop: 2 Inner loop: 3 Inner loop: 4 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test19 #! /bin/bash for ((a=1; a<4; a++)) do echo "Outer loop: $a" for ((b=1;b<100;b++)) do if [ $b -eq 5 ] # 等于5 then break # 暂停 fi echo " Inner loop: $b" done done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
跳出外部循环
break n,默认没有参数为1
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test20 Outer loop: 1 Inner loop: 1 Inner loop: 2 Inner loop: 3 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test20 #! /bin/bash for ((a=1; a<4; a++)) do echo "Outer loop: $a" for ((b=1;b<100;b++)) do if [ $b -eq 4 ] # 等于5 then break 3 # 暂停 fi echo " Inner loop: $b" done done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
书中break写了2,我写了3也没是,看来多写是没关系的。
continue命令
continue用法跟Python这种差不多,也就多了一个continue n就是外部也不执行命令
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test21 Iteration number: 1 Iteration number: 2 Iteration number: 3 Iteration number: 4 Iteration number: 5 Iteration number: 10 Iteration number: 11 Iteration number: 12 Iteration number: 13 Iteration number: 14 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test21 #! /bin/bash for (( var1=1;var1<15;var1++)) do if [ $var1 -gt 5 ] && [ $var1 -lt 10 ] # 5<var1<10 then continue fi echo "Iteration number: $var1" done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
处理循环的输出
循环的最后输出可以在done后面使用管道或者重定向>
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test23.txt The number is 1 The number is 2 The number is 3 The number is 4 The number is 5 The number is 6 The number is 7 The number is 8 The number is 9 shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test23 #! /bin/bash for ((a=1;a<10;a++)) do echo "The number is $a" done > test23.txt shijianzhongdeMacBook-Pro:part_13 shijianzhong$
还有一种通过管道输出
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ ./test24 Alabama is the next place to go Connecticut is the next place to go Illinois is the next place to go North Dakote is the next place to go Tennessee is the next place to go This completes our place to go shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test24 #! /bin/bash for state in "North Dakote" Connecticut Illinois Alabama Tennessee do echo "$state is the next place to go" done | sort echo "This completes our place to go" shijianzhongdeMacBook-Pro:part_13 shijianzhong$
演示实例,查找PATH中的可执行文件
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ echo $PATH /usr/local/opt/mysql@5.7/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin shijianzhongdeMacBook-Pro:part_13 shijianzhong$
从输出可以看出路径是用:分割的,开始抄写代码编写注释
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test25 #! /bin/bash IFS.OLD=$IFS IFS=: for folder in $PATH # 循环读取环境 do echo "$folder:" for file in $folder/* # 读取文件 do if [ -x $file ] # 判断文件是否可执行 then echo " $file" fi done done shijianzhongdeMacBook-Pro:part_13 shijianzhong$
创建多个用户账户,涉及到读取文件操作
shijianzhongdeMacBook-Pro:part_13 shijianzhong$ cat test26 #! /bin/bash input="users.csv" while IFS=',' read -r userid name # 从文件读取后,设置了IFS,读取了两个参数 do echo "adding $userid" useradd -c "$name" -m "$userid" # 创建用户 done < "$input" # 通过<重定向符输入读取的文件 shijianzhongdeMacBook-Pro:part_13 shijianzhong$
上面的代码,重点是while 循环中,分隔符的设置,以及read -r读取重定向符指定的文件
小结
无,稍微写一点点,就是何为条件为真执行,就是退出状态码为0