13.1 for 命令

格式:

  1. for var in list
  2. do
  3. commands
  4. done
  5. for var in list; do
  6. commands
  7. done

13.1.1 读取列表中的值

#!/bin/bash
# basic for command

for test in Alabama Alaska Arizona Arkansas California Colorado
do
    echo The next state is $test
done
  • for 循环结束后, test 会保持 list 中最后一个值
  • 可以在 for 之外使用 test
#!/bin/bash
# testing the for variable after the looping

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"

13.1.2 读取列表中的复杂值

list 中使用单引号:

#!/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
  • list 中的某个值包括空格的话, 使用双引号

13.1.3 从变量读取列表

#!/bin/bash
# using a variable to hold the list

list="Alabama Alaska Arizona Arkansas California Colorado"
list=$list" Connecticut"

for state in $list
do
    echo "Have you ever visited $state?"
done

输出:

$ ./my_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 California?
Have you ever visited Colorado?
Have you ever visited Connecticut?

13.1.4 从命令读取值

#!/bin/bash
# reading values from a file

file="states"

for state in $(cat $file)
do
    echo "Visit beautiful $state"
done

13.1.5 更改字段分隔符

IFS, 内部字段分隔符 (internal field separator), 默认值:

  • 空格
  • 制表符
  • 换行符
IFS=$'\n'

一个可参考的安全实践是在改变 IFS 之前保存原来的 IFS 值, 之后再恢复它。

IFS.OLD=$IFS
IFS=$'\n'
<在代码中使用新的 IFS 值>
IFS=$IFS.OLD

定义多个分隔符:

  • 换行
  • 冒号
  • 分号
  • 双引号
IFS=$'\n':;"

13.1.6 用通配符读取目录

  • 如果目录名或文件名中含有空格, 那么应该使用 "$file" 形式 (双引号)
#!/bin/bash
# iterate through all the files in a directory

for file in /home/rich/test/*
do
    if [ -d "$file" ]
  then
      echo "$file is a directory"
  elif [ -f "$file" ]
  then
      echo "$file is a file"
  fi
done

in 后面可以使用多个值

#!/bin/bash
# iterating through multiple directory

for file in /home/rich/.b* /home/rich/badtest
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

13.2 C 语言风格的 for 命令

13.2.1 C 语言的 for 命令

格式:

  • 引用变量没有使用 $ 符号
for (( variable assignment ; condition ; iteration process ))

例子:

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

注意:

  • 变量赋值可以有空格
  • 条件中的变量不以美元符开头
  • 迭代过程的算式未用 expr 命令格式

使用:

#!/bin/bash
# testing the C-style for loop

for (( i=1; i <= 10; i++ ))
do
    echo "The next number is $i"
done

13.2.2 使用多个变量

  • 但是只能定义一种条件
#!/bin/bash
# multiple variables

for (( a=1, b=10; a <= 10; a++, b-- ))
do
    echo "$a - $b"
done

13.3 while 命令

  • 测试退出状态码

13.3.1 while 的基本格式

while test command
do
    other commands
done
  • 如果退出状态码不变, 那么进入死循环
  • test 可以使用 [] 替代
#!/bin/bash
# while command test

var1=10
while [ $var1 -gt 0 ]
do
    echo $var1
    var1=$[ $var1 - 1 ]
done

13.3.2 使用多个测试命令

  • 只有最后一个测试命令的退出状态码会被用来决定什么时候结束循环
  • 每个测试命令在单独一行
  • 每个测试都会执行

**

#!/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

13.4 until 命令

  • 只有测试命令的退出状态码不为0才会执行循环

格式:

  • 也可以指定多个测试命令
until test commands
do
    other commands
done

使用:

#!/bin/bash
# using the until command

var1=100

until [ $var1 -eq 0 ]
do
    echo $var1
    var1=$[ $var1 - 25 ]
done

13.5 嵌套循环

#!/bin/bash
# nesting for loops

for (( a = 1; a <= 3; a++ ))
do
    echo "Starting loop $a:"
    for (( b = 1; b <= 3; b++ ))
    do
        echo "    Inside loop: $b"
    done
done
#!/bin/bash
# placing a for loop inside a while loop

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=$[ $var1 - 1 ]
done
#!/bin/bash
# using until and while loops

var1=3

until [ $var1 -eq 0 ]
do
    echo "Outer loop: $var1"
    var2=1
    while [ $var2 -lt 5 ]
    do
        var3=$(echo "scale=4; $var1 / $var2" | bc)
        echo "    Inner loop: $var1 / $var2 = $var3"
        var2=$[ $var2 + 1 ]
    done
    var1=$[ $var1 - 1 ]
done

13.6 循环处理文件数据

  • 原例子没有回复 IFS
#!/bin/bash
# changing the IFS value

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

13.7 控制循环

13.7.1 break 命令

1. 跳出单个循环

#!/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"

2. 跳出内部循环

#!/bin/bash
# breaking out of an inner loop

for (( a = 1; a < 4; a++ ))
do
    echo "Outer loop: $a"
    for (( b = 1; b < 100; b++ ))
    do
        if [ $b -eq 5 ]
        then
            break
        fi
        echo "    Inner loop: $b"
    done
done

3. 跳出外部循环

格式:

  • n 指定要跳出的层级
    • 1: 当前循环
break n

使用:

#!/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 5 ]
        then
            break 2
        fi
        echo "    Inner loop: $b"
    done
done

13.7.2 continue 命令

#!/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

也允许使用层级:

continue n

13.8 处理循环的输出

对循环的输出使用管道或进行重定向:

for file in /home/rich/*
do
    if [ -d "$file" ]
  then
      echo "$file is a directory"
  elif
      echo "$file is a file"
  fi
done > output.txt # done | sort

13.9 实例

13.9.1 查找可执行文件

#!/bin/bash
# finding files in the PATH

IFS=:
for folder in $PATH
do
    echo "$folder:"
  for file in $folder/*
  do
      if [ -x $file]
    then
        echo "    $file"
    fi
  done
done

13.9.2 创建多个用户账户

#!/bin/bash
# process new user accounts

input="users.csv"
while IFS=',' read -r userid name
do
    echo "adding $userid"
  useradd -C "$name" -m $userid
done < "$input"

13.10 小结