tartarus's bolg tartarus's bolg
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • pdb

  • make

  • cmake

  • Linux and Unix

    • Linux Tool介绍
    • Useful Linux Tool
    • regular-expression
    • Basic-Shell-Script
    • Using-Structured-Commands
    • More-Structured-Commands
      • 本章内容
      • Looking with the for Command
        • Reading values in a list
        • Reading complex values in a list
        • Reading a list from a variable
        • Reading values from a command
        • Changing the field separator
        • Reading a directory using wildcards
        • Trying the C-Style for Command
        • Using multiple variables
      • Using the while Command
      • Using the until Command
      • Nested Loop
      • Controlling the Loop
      • Redirecting and Piping Loop
    • Handling-User-Input
    • Presenting-Data
    • Script-Control
  • Basic_Software
  • Linux and Unix
tartarus
2023-05-23
目录

More-Structured-Commands

# 本章内容

  • Looping with the for statement
  • Using the while statement
  • Using the until statement
  • Nested loops
  • Controlling the loops
  • Redirecting and piping loop

# Looking with the for Command

for 在 shell script 中的语法为:

for var in list
do
  commands
done
1
2
3
4

或者写为:

for var in list; do
  commands
done
1
2
3

# Reading values in a list

for 最常见的使用方法是迭代遍历 list ,举例如下:
(注意 list 中不同的值用默认用空格分隔,详情看 IFS,如果同一个值中间有空格,比如 New York,需要加 double quotation,即 "New York")

$ cat script.sh
#!/bin/bash 
# basic for command 
for test in Alabama Alaska Arizona Arkansas California Colorado 
do 
  echo The next state is $test 
done%                                                                                                                 
$ ./script.sh
The next state is Alabama
The next state is Alaska
The next state is Arizona
The next state is Arkansas
The next state is California
The next state is Colorado
1
2
3
4
5
6
7
8
9
10
11
12
13
14

遍历结束后,上面例子中的 test 变量不会消失仍可继续使用 (这和大部分的编程语法不同),它会存储 list 中的最后一个值,一直到 script 运行结束;当然也可以在遍历结束后修改 test 变量的值。

$ cat script.sh
#!/bin/bash 
# basic for command 
for test in Alabama Alaska Arizona Arkansas California Colorado 
do 
  echo The next state is $test 
done

echo "The last state we visited was $test" 
test=Connecticut 
echo "Wait, now we're visiting $test"%

$ ./script.sh
The next state is Alabama
The next state is Alaska
The next state is Arizona
The next state is Arkansas
The next state is California
The next state is Colorado
The last state we visited was Colorado
Wait, now we're visiting Connecticut
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Reading complex values in a list

single quotation 中的字符串在 list 中会被解释为一个没有分隔的整体:

$ cat script.sh
#!/bin/bash
# another example of how not to use the for command 
for test in I don't know if this'll work 
do echo "word:$test" 
done%                                                                                                                 
$ ./script.sh
word:I
word:dont know if thisll
word:work
1
2
3
4
5
6
7
8
9
10

解决办法:

  • 可以使用 backslash 来 escape single quotation
  • 也可以使用 double quotation 将 single quotation 括起来
$ cat script.sh
#!/bin/bash
# another example of how not to use the for command 
for test in I don\'t know if "this'll" work 
do echo "word:$test" 
done%                                                                                                                 
$ ./script.sh
word:I
word:don't
word:know
word:if
word:this'll
word:work
1
2
3
4
5
6
7
8
9
10
11
12
13

# Reading a list from a variable

list 除了可以是不同 item 的组合之外,list 也可以是一个变量,如下面的例子所示:

#!/bin/bash 
# using a variable to hold the list 
list="Alabama Alaska Arizona Arkansas Colorado" 
# 将一个新的item添加到list中的方法
list=$list" Connecticut" 
for state in $list 
do 
  echo "Have you ever visited $state?" 
done
1
2
3
4
5
6
7
8
9

提示

这里值得注意的是,往一个 list 变量中加入一个新的 item 的方法: list=$list" Connecticut"

# Reading values from a command

list 也可以是使用 command substitution 读取的内容

# space, newline都是默认的field separator,具体看下一节
$ cat states.txt
Alabama 
Alaska 
Arizona 
Arkansas 
Colorado 
Connecticut Delaware Florida Georgia

$ cat script.sh
#!/bin/bash 
# using a variable to hold the list 
file="states.txt"
# using command substitution
for state in $(cat $file)
do 
  echo "Have you ever visited $state?" 
done

$ ./script.sh
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?
Have you ever visited Delaware?
Have you ever visited Florida?
Have you ever visited Georgia?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# Changing the field separator

IFS (Internal Field Seperator): IFS 中定义了所有 field separators 的所有字符,bash shell 将用这些字符作为分隔符。

默认的 IFS 有:newline, space, tab

可以通过临时的改变 IFS 使得 for 读取 list 的 item 发生改变:

$ cat states.txt
Alabama 
Alaska 
Arizona 
Arkansas 
Colorado 
Connecticut Delaware Florida Georgia
$ cat script.sh
#!/bin/bash 
# using a variable to hold the list 
file="states.txt"
# 改变了分隔符只有newline
IFS=$'\n'
for state in $(cat $file)
do 
  echo "Have you ever visited $state?" 
done

$ ./script.sh
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 Delaware Florida Georgia?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

注意

A safe practice to get into is to save the originalIFS value before changing it and then restore it when you're finished:

IFS.OLD=\$IFS 
IFS=\$'\n'
<use the new IFS value in code>
IFS=$IFS.OLD
1
2
3
4

如果需要添加多个 IFS,直接把它们放一起即可:
IFS=$'\n':;"
(使用 newline , : , : , " 作为 IFS)

# Reading a directory using wildcards

$ cat script.sh
#!/bin/bash 
# iterate through all the files in directory '/'
# list中还可以有多个文件或者文件夹路径
for file in /* /home/tartarus/badtest
do 
  # $file需要用double quotation注释,因为有些文件名中可能有space,错误看下一个例子
  if [ -d "$file" ] 
  then
    echo "$file is a directory" 
  elif [ -f "$file" ] 
  then
    echo "$file is a file" 
  fi 
done

$ ./script.sh
/bin is a directory
/boot is a directory
/cdrom is a directory
/dev is a directory
/etc is a directory
...
/home/tartarus/badtest doesn't exist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

读取上面例子中的 file 变量时 ( if [ -d "$file" ] ),最好使用将读取的值放到 double quotation 中,否则可能会因为文件名中有空格而产生错误:

$ ls -r demo
'hello world'
$ cat script.sh
#!/bin/bash 
# iterate through all the files in a directory 
for file in /tmp/demo/* 
do 
  if [ -d $file ] 
  then
    echo "$file is a directory" 
  elif [ -f $file ] 
  then
    echo "$file is a file" 
  fi 
done

$ ./script.sh
./script.sh: line 5: [: /tmp/demo/hello: binary operator expected
./script.sh: line 8: [: /tmp/demo/hello: binary operator expected
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Trying the C-Style for Command

在 Bash shell 中也可以使用 C 风格的 for 循环:

for (( variable assignment ; condition ; iteration process ))
1

举例:

$ cat script.sh
#!/bin/bash 
# testing the C-style for loop 
for (( i=1; i <= 10; i++ )) 
do 
  echo "The next number is $i" 
done

$ ./script.sh
The next number is 1
The next number is 2
The next number is 3
The next number is 4
The next number is 5
The next number is 6
The next number is 7
The next number is 8
The next number is 9
The next number is 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

提示

C 风格 for 循环的双括号中:

  • 变量赋值语句的等号左右可以有空格
  • 使用变量时也不需要加 dollar sign
  • 数学运算也不需要使用 expr 命令格式

# Using multiple variables

C 风格的 for 循环中还可以使用多个变量,但是条件判断只能有一个变量:

$ cat script.sh
#!/bin/bash 
# multiple variables: 可以有多个变量,但是条件判断时只能有一个变量
for (( a=1, b=10; a <= 10; a++, b-- )) 
do 
  echo "$a - $b" 
done%                                                                                                                                                      
$ ./script.sh
1 - 10
2 - 9
3 - 8
4 - 7
5 - 6
6 - 5
7 - 4
8 - 3
9 - 2
10 - 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Using the while Command

while 语句判断 command 是否运行成功,如果退出码为 0,则执行循环中的命令。

语法:

while command # 也可以使用 while [ condition ]
do
  other commands
done
1
2
3
4

注意

while 循环的关键是状态码,如果状态码一直为 0,那么 while 循环就变成了死循环。

其中,常把 command 设置一个 test 的判断条件, test 命令判断 condition 是否成立,如果成立状态码为 0,执行 commands ; 否则退出循环。

while test condition # 也可以使用 while [ condition ]
do
  other commands
done
1
2
3
4

举例:

$ cat script.sh
#!/bin/bash 
# while command test 
var1=10 
while [ $var1 -gt 0 ]  // 等价于: test $var1 -gt 0
do 
  echo $var1 
  var1=$[ $var1 - 1 ] 
done%                                                                                                                                                      
$ ./script.sh
10
9
8
7
6
5
4
3
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

提示

while 中可以有多条 command,但是只由最后命令最好是一个 test conditon 来决定继续循环还是退出,每条命令都要占一行。

举例:

$ cat script.sh
#!/bin/bash 
# testing a multicommand while loop 
var1=10 
while echo $var1 
      [ $var1 -ge 0 ] 
do 
  echo "This is inside the loop" 
  var1=$[ $var1 - 1 ] 
done

$ ./script.sh
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# Using the until Command

until 的使用方式和 while 恰好相反。 如果 commands 中的最后一条命令的状态码返回为 0,则退出循环,否则进行循环。

until 语法:

until commands
do
  commands
done
1
2
3
4

举例:

$ cat script.sh
#!/bin/bash 
# using the until command 
var1=100 
until echo $var1 
      [ $var1 -eq 0 ] 
do 
  echo Inside the loop: $var1
  var1=$[ $var1 - 25 ] 
done

$ ./script.sh
100
Inside the loop: 100
75
Inside the loop: 75
50
Inside the loop: 50
25
Inside the loop: 25
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Nested Loop

无论是 C 风格的 for 循环,还是 while, until 循环,都可以嵌套使用。但是嵌套使用容易引起错误。

举例:使用嵌套的 for 循环读取文件 etc/passwd

IFS_old=$IFS

IFS=$'\n'
for entry in $(cat /etc/passwd)
do
  IFS=$:
  echo "Values in $entry –"
  for item in $entry
    do
      echo $item 
    done
done
IFS=$IFS_old

$ ./script.sh
Values in root:x:0:0:root:/root:/bin/bash –
root
x
0
0
root
/root
/bin/bash
Values in daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin –
daemon
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# Controlling the Loop

Bash 脚本中也可以使用 break 和 continue 两个字段来控制循环:

  • break

    • break 可以使用于任意类型的循环 ( for , while , until )
    $ cat script.sh
    #!/bin/bash 
    # breaking out of a for loop 
    for var1 in 1 2 3 4 5 6 7 8 9 10 
    do 
      if [ $var1 -eq 5 ] 
      then
        break 
      fi 
      echo "Iteration number: $var1" 
    done 
      
    echo "The for loop is completed"%                                                                                                                          
    $ ./script.sh
    Iteration number: 1
    Iteration number: 2
    Iteration number: 3
    Iteration number: 4
    The for loop is completed
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    • break n 可以跳出 n 个循环
    $ cat script.sh
    #!/bin/bash 
    # breaking out of an outer loop 
    for (( a = 1; a < 4; a++ )) 
    do 
      echo "Outer loop: $a" 
      for (( b = 1; b < 100; b++ )) 
      do 
        if [ $b -gt 4 ] 
        then
          break 2 
        fi 
        echo " Inner loop: $b" 
      done 
    done
    
    $ ./script.sh
    Outer loop: 1
     Inner loop: 1
     Inner loop: 2
     Inner loop: 3
     Inner loop: 4
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • continue 可以使用于任意类型的循环 ( for , while , until )

$ cat script.sh
#!/bin/bash 
# using the continue command 
for (( var1 = 1; var1 < 15; var1++ )) 
do 
  if [ $var1 -gt 5 ] && [ $var1 -lt 10 ] 
  then
    continue 
  fi 
  echo "Iteration number: $var1" 
done

$ ./script.sh
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • continue n (跳过一个循环的 n 次遍历)
$ cat script.sh
#!/bin/bash 
# continuing an outer loop 
for (( a = 1; a <= 5; a++ )) 
do 
  echo "Iteration $a:" 
  for (( b = 1; b < 3; b++ )) 
  do 
    if [ $a -gt 2 ] && [ $a -lt 4 ] 
    then
      continue 2 
    fi 
  var3=$[ $a * $b ] 
  echo " The result of $a * $b is $var3" 
  done 
done

$ ./script.sh
Iteration 1:
The result of 1 * 1 is 1
The result of 1 * 2 is 2
Iteration 2:
The result of 2 * 1 is 2
The result of 2 * 2 is 4
Iteration 3:
Iteration 4:
The result of 4 * 1 is 4
The result of 4 * 2 is 8
Iteration 5:
The result of 5 * 1 is 5
The result of 5 * 2 is 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# Redirecting and Piping Loop

可以将一个循环的输出进行重定向或者 pipe。

  • 将 for 循环到输出重定向到一个文件中
#/bin/bash
for file in /home/tartarus/* 
do
  if [ -d "$file" ] 
  then
    echo "$file is a directory" 
  else 
    echo "$file is a file" 
  fi 
done > output.txt
1
2
3
4
5
6
7
8
9
10
  • 将一个文件作为输出重定向到 while 循环中
    • read 可以将一个文件中的一行作为输入读取到一个或者多个变量中
$ cat users.csv
rich,Richard Blum 
christine,Christine Bresnahan 
barbara,Barbara Blum 
tim,Timothy Bresnahan

$ cat script.sh
#/bin/bash
# 这里使用了读取文件到循环中的一个tricky
while IFS=',' read loginname fullname 
do 
  echo "Loginname:$loginname Fullname:$fullname"; 
done < users.csv

$ ./script.sh
Loginname:rich Fullname:Richard Blum 
Loginname:christine Fullname:Christine Bresnahan 
Loginname:barbara Fullname:Barbara Blum 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 将 for 循环到输出作为管道传递给另一个命令作为输入
#/bin/bash
for file in /home/tartarus/* 
do
  if [ -d "$file" ] 
  then
    echo "$file is a directory" 
  else 
    echo "$file is a file" 
  fi 
done | sort
1
2
3
4
5
6
7
8
9
10
上次更新: 12/27/2023, 8:55:47 AM
Using-Structured-Commands
Handling-User-Input

← Using-Structured-Commands Handling-User-Input→

Theme by Vdoing | Copyright © 2023-2023 tartarus | CC BY-NC-SA 4.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式