《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

posted @ 2020-05-22 14:31  就是想学习  阅读(189)  评论(0编辑  收藏  举报