从零开始学shell之复杂的结构化命令

shell May 22, 2019

一 前言

本节学习复杂的结构化命令,主要有三个:for,while,until命令。通过它们来实现复杂的结构化命令。
在命令式的编程模型中,一般会有三种程序结构:

  1. 顺序式:按照顺序执行程序
  2. 条件式:根据条件执行程序
  3. 循环式:根据条件循环执行程序 本节的学习就是为了实现循环的程式。

二 for命令

for一般是为了重复执行一系列指令。比如处理某个目录下所有文件,系统上的所有用户或者某个文件中的所有行。

2.1 基本格式

for命令的基本格式如下:

for var in list
do
commands
done

在list参数中,需要提供迭代用到的一系列值。可以通过不同的方法指定列表中的值。

2.2 读取列表中的值

for命令最基本用法就是遍历for命令自身所定义的一系列值。
例子:

for test in Alabama Alaska Arizona Arkansas California Colorado
do
echo The next state is $test
done

ps:
如果in 后面的值有特殊字符,单引号,双引号等,那么整个语句就会错误。有两个解决办法

  1. 转义字符
  2. 使用双引号来定义用到的单引号值

for是通过空格来区分循环的值,如果循环的值里面包含空格,需要用双引号括起来。

for var in "New York" Beijing Tokyo
do
    echo "$var"
done

对于循环变量,当for循环退出后仍然可以使用,它的值是最后一次迭代的值。

for var in "New York" Beijing Tokyo
do
    echo "$var"
done
# var的值是Tokyo

2.3 从变量读取列表

通常当shell脚本遇到的情况是,将一系列的值存储在一个变量中,然后需要遍历变量中的整个列表。也可以通过for命令完成这个任务。

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

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

2.4 从命令去读列表

我们还可以使用命令的输出作为for循环的输入

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

$ cat states
Alabama
Alaska
Arizona
Arkansas
Colorado
Connecticut
Delaware
Florida
Georgia

2.5 更改字段分隔符

shell是如何区分数据字段的呢?答案是通过一个环境变量IFS,叫做内部字段分隔符(internal field separator)。IFS环境变量定义了bash shell用作字段分隔符的一系列字符。默认情况下,bash shell会将下列字符当作字段分隔符:

  1. 空格
  2. 制表符
  3. 换行符

我们可以通过在脚本中修改IFS的值来改变shell区分数据字段的行为。

file="states"
IFS=$'\n'
for state in $(cat $file)
do
echo "Visit beautiful $state"
done

如下输出:
Visit beautiful Alabama
Visit beautiful Alaska
Visit beautiful Arizona
Visit beautiful Arkansas
Visit beautiful Colorado
Visit beautiful Connecticut
Visit beautiful Delaware
Visit beautiful Florida
Visit beautiful Georgia
Visit beautiful New York
Visit beautiful New Hampshire
Visit beautiful North Carolina

现在的shell脚本就能处理带有空格的行了。
ps:

  1. 我们还可以在shell脚本中处理IFS,保留IFS旧值,然后在脚本结束后修改回来
  2. 指定多个分隔符,只要将他们在赋值行串起来就好了。eg:IFS=$'\n':;"  这个赋值会使用换行符,冒号,分号,双引号作为分隔符。

2.6 使用通配符读取目录

我们可以使用通配符让for命令自动遍历目录中的文件。

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

三 while命令

while某种意义上是if-then语句和for循环的混合体。while命令允许定义一个要测试的命令,然后循环执行一组命令,只要定义的命令返回的状态码是0。当返回的状态码是非0值,while循环就会停止执行那组命令。

3.1 while循环的基本格式

while test command 
do 
    other commands 
done 

常见的是用 test command 检查循环中用到的shell的变量

var=10

while [ $var -gt 0 ]
do
    echo $var
    var=$[ $var + 1 ]
done

四 until命令

until命令和while命令正好相反。until命令要求你指定一个通常返回非零状态码的测试命令。只有当测试命令的退出状态码不为0,bash shell才会执行循环中列出的命令。一旦测试命令返回的状态码为0,循环就结束了。
ps:until可以理解为直到事情变得好(状态0)才停止。while是事情变得糟糕了(状态非0)就不要再做了。

五 控制循环

循环开始用,我们也可以通过一些命令控制循环的逻辑。

5.1 break命令

break命令的作用是跳出循环,它有两个用法

  1. 跳出一个循环
  2. 跳出n层循环

5.1.1 跳出一层循环

for var in 1 2 3 4 5 6 7 8 9 
do 
    if [ var -eq 5 ]
    then 
        break
    fi
done    

5.1.2 跳出n层循环

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

5.2 continue命令

continue可以提前中止某次循环中的命令,但并不会完全终止整个循环。
continue也提供两种方式:

  1. 中止一层循环
  2. 中止n层循环

5.2.1 中止一层循环

var=0 
while [ $var -lt 15 ]
do
    var=$[ $var + 1 ]
    if [ $var -gt 5 ] && [ $var -lt 10 ]
    then 
        continue
    fi
    echo "var is $var"
done     

5.2.2 中止n层循环

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

总结

学习了循环的写法,就可以写出很多有趣的shell了。

zzx

There is my place for writing,coding and reading