【Linux命令行与Shell脚本编程】十二,结构化命令

本文详细介绍了LinuxShell脚本中的结构化命令,包括if-then语句、if-then-else和elseif语句,以及test命令的使用,如数值、字符串和文件的比较。此外,还涵盖了复合测试条件、case命令、for循环和while/until循环的用法,以及如何在循环中处理文件数据和控制循环流程。
摘要由CSDN通过智能技术生成

Linux命令行与Shell脚本编程

第十二章 结构化命令



二,结构化命令

允许脚本根据条件跳过部分命令,改变执行流程。

1,if-then 语句

语法:
    if command
    then
        commands
    fi  
或 通过把分号(;)放在待求值的命令尾部,可以将then语句写在同一行
    if command; then
       commands
    fi
bash shell的if语句会运行if之后的命令。
如果该命令的 退出状态码为 0,那么位于then部分的命令就会被执行。
如果该命令的退出状态码是其他值,则 then部分的命令不会被执行,bash shell会接着处理脚本中的下一条命令。
fi语句用来表示if-then语句到此结束。

$ cat test1.sh
    #!/bin/bash
    # testing the if statement
    if pwd
    then
         echo "it worked"
    fi
$ ./test1.sh
    /home/christine/scripts
    It worked

2,if-then-else语句

提供了 commond 执行返回 非0退出状态码时 的处理逻辑.
    if command
    then
       commands
    else
       commands
    fi

3,elseif语句

if command1
then
   commands
elif command2
then
    more commands
fi

4,test命令

    test condition
test命令 在bash shell中 可以 使用if-then语句测试退出状态码之外的条件.
test命令可以在if-then语句中测试不同的条件。如果test命令中列出的条件成立,那么test命令就会退出并返回退出状态码0。

test命令和测试条件可以判断3类条件。
    ·数值比较
    ·字符串比较
    ·文件比较

test 的 if-then语法:        
    if test condition
    then
       commands
    fi
如果加入了条件,则test命令会测试该条件
如果不写test命令的 condition部分,则会以非0的退出状态码退出并执行else代码块语句.

可以使用test命令确定变量中是否为空.
    if test $var
    then
        command
    else
        command
    fi
bash shell提供了另一种条件测试方式,
    第一个方括号之后和第二个方括号之前必须留有空格!!!
    if [ condition ]
    then
       commands
    fi

4.1数值比较

两个值时可用的条件参数。可用于数值和数值型变量.
对于条件测试,bash shell只能处理整数。尽管可以将浮点值用于某些命令(比如echo),但在条件测试下无法正常工作。
表达式描述
n1-eq n2检查n1是否等于n2
n1-ge n2检查n1是否大于或等于n2
n1-gt n2检查n1是否大于n2
n1-len2检查n1是否小于或等于n2
n1-ltn2检查n1是否小于n2
n1 -ne n2检查n1是否不等于n2

4.2,字符串比较

条件测试还允许比较字符串值。
表达式描述
str1=str2检查str1是否和str2相同
str1!= str2检查str1是否和str2不同
str1<str2检查str1是否小于str2
str1 > str2检查str1是否大于str2
-n str1检查str1的长度是否不为0
-z str1检查str1的长度是否为0
4.2.1,字符串相等性
比较测试会将所有的标点和大小写情况都考虑在内。
4.2.2,字符串顺序
使用测试条件的大于或小于功能时:
    大于号和小于号必须转义,否则shell会将其视为重定向符,将字符串值当作文件名。
    大于和小于顺序与 sort命令 所采用的不同。

!!! 比较测试中 字符串在比较的时候使用的是每个字符的 Unicode编码值。sort命令使用的是系统的语言环境设置中定义的排序顺序。
在比较测试中,大写字母被认为是小于小写字母的。但sort命令正好相反。
4.2.3,字符串大小
-n和-z可以很方便地用于检查一个变量是否为空:
    非空 if [ -n "$string1" ]
    为空 if [ -z "$string2" ]

!!! 未定义的变量长度也为 0;

4.3,文件比较

shell编程中 强大且用得最多的 比较形式。允许测试Linux文件系统中文件和目录的状态
表达式描述
-d file检查file是否存在 且为目录
-e file检查file是否存在
-f file检查file是否存在 且为文件
-r file检查file是否存在 且可读
-s file检查file是否存在 且非空
-w file检查file是否存在 且可写
-x file检查file是否存在 且可执行
-O file检查file是否存在 且属当前用户所有
-G file检查file是否存在 且默认组与当前用户相同
file1 -nt file2检查file1是否比file2新
file1 -ot file2检查file1是否比file2旧
4.3.1,检查目录
如果打算将文件写入目录或是准备切换到某个目录,那么先测试一下:
    $ cat jump_point.sh
        #!/bin/bash
        jump_directory=/home/Torfa
        if [ -d $jump_directory ]
        then
             echo "The $jump_directory directory exists."
             cd $jump_directory
             ls
        else
             echo "The $jump_directory directory does NOT exist."
        fi
    $ ./jump_point.sh
        The /home/Torfa directory does NOT exist.
4.3.2.检查对象是否存在
-e测试允许在使用文件或目录前先检查其是否存在:
    $ cat update_file.sh
        #!/bin/bash
        location=$HOME
        file_name="sentinel"
        if [ -d $location ]
        then
             echo "OK on the $location directory"
             echo "Now checking on the file, $file_name..."
             if [ -e $location/$file_name ]
             then
                  echo "OK on the file, $file_name."
                  echo "Updating file's contents."
                  date >> $location/$file_name
             else
                  echo "File, $location/$file_name, does NOT exist."
                  echo "Nothing to update."
             fi
        else
             echo "Directory, $location, does NOT exist."
             echo "Nothing to update."
        fi
    $ ./update_file.sh
        OK on the /home/christine directory
        Now checking on the file, sentinel...
        File, /home/christine/sentinel, does NOT exist.
        Nothing to update.
    $ touch /home/christine/sentinel
    $ ./update_file.sh
        OK on the /home/christine directory
        Now checking on the file, sentinel...
        OK on the file, sentinel.
        Updating file's contents.
4.3.3,检查文件 -f
-e测试可用于文件和目录。如果要确定指定对象为文件,那就必须使用-f测试:
4.4.4.检查是否可读 -r
尝试从文件中读取数据之前,最好先使用-r测试检查一下文件是否可读:
4.4.5,检查空文件
应该用-s测试检查文件是否为空,尤其是当你不想删除非空文件时。
4.4.6.检查是否可写
-w测试可以检查是否对文件拥有可写权限
4.4.7.检查文件是否可以执行
-x测试可以方便地判断文件是否有执行权限
4.4.8.检查所有权
-O测试可以轻松地检查你是否是文件的属主
4.4.9.检查默认属组关系
-G测试可以检查文件的属组,如果与用户的默认组匹配,则测试成功。
4.4.10.检查文件日期
-nt测试会判定一个文件是否比另一个文件更新.如果文件较新,那意味着其文件创建日期更晚
-ot测试会判定一个文件是否比另一个文件更旧.如果文件较旧,则意味着其文件创建日期更早

5,复合测试条件 && ||

if-then语句允许使用布尔逻辑将测试条件组合起来。可以使用以下两种布尔运算符。
    ·[ condition1 ] && [ condition2 ]
    ·[ condition1 ] || [ condition2 ]

6,if-then高级特性 () (()) [[ ]]

bash shell 提供了3个可在if-then语句中使用的高级特性。
    ·在子shell中执行命令的 单括号。
    ·用于数学表达式的 双括号。
    ·用于高级字符串处理功能的 双方括号。接下来将详细描述各个特性。

6.1,使用单括号

单括号允许在if语句中使用子shell,语法:
    (command)

6.2,使用双括号

双括号命令允许在比较过程中使用高级数学表达式。test命令在进行比较的时候只能使用简单的算术操作。双括号命令提供了更多的数学符号,语法:
    (( expression ))
双括号命令既可以在if语句中使用,也可以在脚本中的普通命令里用来赋值

除了test命令使用的标准数学运算符 外的 其他运算符:
符号描述
val++后增
val–后减
++val先增
–val先减
!逻辑求反
~位求反
**幂运算
<<左位移
>>右位移
&位布尔AND
|位布尔OR
&&逻辑AND
||逻辑OR

6.3,使用双方括号

双方括号命令提供了针对字符串比较的高级特性。语法:
    [[ expression ]]
expression可以使用 test命令中的标准字符串比较。还提供了 模式匹配。

双等号(==)会将右侧的字符串视为一个模式并应用模式匹配规则。

在进行模式匹配时,可以定义通配符或正则表达式(参见第20章)来匹配字符串
    $ cat DoubleBrackets.sh
        #!/bin/bash
        if [[ $BASH_VERSION == 5.* ]]
        then
             echo "You are using the Bash Shell version 5 series."
        fi
    $ ./DoubleBrackets.sh
        You are using the Bash Shell version 5 series.

7,case命令

case命令会采用列表格式来检查变量的多个值:双分分割case选项;
    case variable in
    pattern1 | pattern2) commands1;;
    pattern3) commands2;;
    *) default commands;;
    esac
如果变量与模式匹配 shell就会执行为该模式指定的命令。
可以通过 竖线运算符 | 在一行中分隔出多个模式。
星号会捕获所有与已知模式不匹配的值。

8,for命令

重复多个命令直至达到某个特定条件.for命令允许创建遍历一系列值的循环。
语法:
    for var in list
    do
       commands
    done
    或
    for var in list; do
        comands
    done 

8.1,读取列表中的值

遍历其自身所定义的一系列值
    $ cat test1
        #!/bin/bash
        for test in Alabama Alaska Arizona Arkansas California Colorado
        do
           echo The next state is $test
        done
    $ ./test1
        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
在最后一次迭代结束后,$test变量的值在shell脚本的剩余部分依然有效。它会一直保持最后一次迭代时的值(除非做了修改)!!!

8.2,读取列表中的复杂值

引号带来的复杂情况,shell看到 列表值中的单引号并尝试使用 它们 来定义一个单独的数据值.
    $ cat badtest1
        #!/bin/bash
        for test in I don't know if this'll work
        do
           echo "word:$test"
        done
    $ ./badtest1
        word:I
        word:dont know if thisll
        word:work
        
    解决这个问题。
        ·使用转义字符(反斜线)将单引号转义。
        ·使用双引号来定义含有单引号的值。
    #!/bin/bash
    for test in I don\'t know if "this'll" work
    do
       echo "word:$test"
    done

多单词值:for循环假定各个值之间是以空格分隔的,存在空格的单词,必须放入双引号内.
    $ cat test3
    #!/bin/bash
    for test in Nevada "New Hampshire" "New Mexico" "New York"
    do
       echo "Now going to $test"
    done

8.3,从变量中读取列表值

将一系列值集中保存在了一个变量中,然后需要遍历该变量中的整个值列表
    $ cat test4
    #!/bin/bash
    list="Alabama Alaska Arizona Arkansas Colorado"
    list=$list" Connecticut"  ## 向$list变量包含的值列表中追加(拼接)了一项。这是向变量中已有的字符串尾部添加文本的一种常用方法。
    for state in $list
    do
       echo "Have you ever visited $state?"
    done

8.4,从命令中读取列表值

生成值列表的另一种途径是使用命令的输出。
    $ cat test5
        #!/bin/bash
        file="states.txt"  ##不包含路径的文件名赋给了变量。要求文件和脚本位于同一个目录中。否则则需要使用完整路径名(绝对/相对路径)来引用文件位置
        for state in $(cat $file)
        do
           echo "Visit beautiful $state"
        done
    $ cat states.txt
        New York
        New Hampshire
    $ ./test5
         Visit beautiful New 
         Visit beautiful York
         Visit beautiful New 
         Visit beautiful Hampshire
文件中每个值各占一行,而不是以空格分隔。for命令仍然以每次一行的方式遍历cat命令的输出。
这并没有解决数据中含有空格的问题。如果列出了一个名字中有空格的值,则for命令仍然会用空格来分隔值。

8.5,更改字段分隔符

特殊的环境变量 IFS(internal field separator,内部字段分隔符)。
IFS环境变量定义了 bash shell 用作字段分隔符的一系列字符。在默认情况下,bash shell会将下列字符视为字段分隔符。
    ·空格
    ·制表符
    ·换行符
改 IFS的值,使其只能识别换行符:
    IFS=$'\n'
该语句加入脚本,告诉bash shell忽略数据中的空格和制表符:
    $ cat test5b
         #!/bin/bash
         file="states.txt"
         IFS=$'\n'
         for state in $(cat $file)
         do
            echo "Visit beautiful $state"
         done
    $ ./test5b
         Visit beautiful New York
         Visit beautiful New Hampshire
安全的做法是在修改IFS之前保存原来的IFS值,之后再恢复它!!!
    IFS.OLD=$IFS
    IFS=$'\n'
    ... ...
    IFS=$IFS.OLD
指定多个IFS字符,在赋值语句中将这些字符写在一起.
该语句会将换行符、冒号、分号和双引号作为字段分隔符:
    IFS=$'\n:;"'

8.6,使用通配读取目录

可以用 for命令来自动遍历目录中的文件。
必须在文件名或路径名中使用通配符,会强制 shell 使用文件名通配符匹配(file globbing)。

文件名通配符匹配是 生成与指定通配符匹配的文件名或路径名 的过程。

    $ cat test6
        #!/bin/bash
        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
    $ ./test6
        /home/rich/test/dir1 is a directory
        /home/rich/test/myprog.c is a file

在 Linux中,目录名和文件名中包含空格是合法的。若目录名存在空格,应该将$file变量放入双引号内。
    if [ -d "$file" ]
在test命令中,bash shell会将额外的单词视为参数,引发错误。
    
可以在for命令中列出多个目录通配符:
    #!/bin/bash
    for file in /home/rich/.b* /home/rich/badtest
    do
        ....
    done

9,C语言风格的for命令

C语言中,for循环通常会定义一个变量,然后在每次迭代时更改。程序员通常会将这个变量用作计数器,每次迭代时让计数器增1或减1。bash的for命令也提供了这个功能。

9.1,bash shell 与 C语言中的for命令

C语言中的for命令包含循环变量初始化、循环条件以及每次迭代时修改变量的方法
当指定的条件不成立时,for循环就会停止。迭代条件使用标准的数学符号定义。
    for (i = 0; i < 10; i++)
    {
       printf("The next number is %d\n", i);
    }

bash中 仿C语言的for循环 的基本格式:
    for (( variable assignment ; condition ; iteration process ))
仿C语言的for循环的格式使用了C语言风格而不是shell风格的变量引用方式:
    for (( a = 1; a < 10; a++ ))
与bash shell标准的for命令并不一致: !!!!
    ·变量赋值可以有空格
    ·迭代条件中的变量不以美元符号开头
    ·迭代过程的算式不使用expr命令格式
示例:
    $ cat test8
        #!/bin/bash
        for (( i=1; i <= 3; i++ ))
        do
           echo "The next number is $i"
        done
    $ ./test8
        The next number is 1
        The next number is 2
        The next number is 3

9.2,使用多个变量 类C

循环会单独处理每个变量,可以为每个变量定义不同的迭代过程。可以使用多个变量,但只能在for循环中定义一种迭代条件:
    $ cat test9
        #!/bin/bash
        for (( a=1, b=10; a <= 3; a++, b-- ))
        do
           echo "$a - $b"
        done
    $ ./test9
        1 - 10
        2 - 9
        3 - 8

10,while命令 返回码为0循环

while命令在某种程度上糅合了 if-then语句和 for循环。

while命令允许定义一个要测试的命令,只要该命令返回的退出状态码为 0,就循环执行一组命令。
会在每次迭代开始时测试 test命令,如果 test命令返回 非0退出状态码,while命令就会停止执行循环。

10.1,while的基本格式

语法:
    while test command
    do
     other commands
    done
如果退出状态码不发生变化,那while循环就成了死循环。
    $ cat test10
        #!/bin/bash
        var1=3
        while [ $var1 -gt 0 ]
        do
           echo $var1
           var1=$[ $var1 - 1 ]
        done
    $ ./test10
        3
        2
        1

10.2,使用多个测试命令

while命令允许在while语句行定义多个测试命令。只有最后一个测试命令的退出状态码会被用于决定是否结束循环。
    $ cat test11
         #!/bin/bash
         var1=3
         while echo $var1
               [ $var1 -ge 0 ]
         do
            echo "This is inside the loop"
            var1=$[ $var1 - 1 ]
         done
    $ ./test11
        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

11,until命令 返回码非0循环

until命令要求指定一个返回非0退出状态码的测试命令.与while命令工作的方式相反.
格式:
    until test commands
    do
       other commands
    done
示例:
    $ cat test12
        #!/bin/bash
        var1=100
        until [ $var1 -eq 0 ]
        do
           echo $var1
           var1=$[ $var1 - 25 ]
        done
    $ ./test12
        100
        75
        50
        25

12,嵌套循环

循环语句可以在循环内使用任意类型的命令包括其他循环命令.

13,循环处理文件数据

修改IFS环境变量,能强制for命令将文件中的每一行都作为单独的条目来处理,即便数据中有空格也是如此。
从文件中提取出单独的行后,可能还得使用循环来提取行中的数据。


处理/etc/passwd文件。这要求你逐行遍历该文件,将IFS变量的值改成冒号,以便分隔开每行中的各个字段:
    $ cat test8
        #!/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
        IFS=$IFS.OLD
    $ ./test8
        Values in rich:x:501:501:Rich Blum:/home/rich:/bin/bash -
           rich
           x
           501
           501
           Rich Blum
           /home/rich
           /bin/bash
        Values in katie:x:502:502:Katie Blum:/home/katie:/bin/bash -
           katie
           x
           502
           502
           Katie Blum
           /home/katie
           /bin/bash

14,循环控制 break continue

有两个命令可以控制循环的结束时机。
    ·break命令
    ·continue命令

14.1,break命令

break命令是退出循环的一种简单方法。你可以用break命令退出任意类型的循环,包括while循环和until循环。
1,跳出单个循环
shell在执行break命令时会尝试跳出当前正在执行的循环:
    $ cat test17
        #!/bin/bash
        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"
    $ ./test17
        Iteration number: 1
        Iteration number: 2
        Iteration number: 3
        Iteration number: 4
        The for loop is completed
2,跳出内存循环
默认跳出内层循环
3,跳出外层循环
位于内层循环,需要结束外层循环。break命令接受单个命令行参数
    break n
默认情况下,n为1,表明跳出的是当前循环。如果将n设为2,那么break命令就会停止下一级的外层循环;
    $ cat test20
        #!/bin/bash
        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
    $ ./test20
        Outer loop: 1
           Inner loop: 1
           Inner loop: 2
           Inner loop: 3
           Inner loop: 4

14.2,continue命令

continue命令可以提前中止某次循环,但不会结束整个循环;
continue命令 也允许通过命令行参数指定要继续执行哪一级循环,n定义了要继续的循环层级:
    continue n

15,处理循环输出 >

可以对循环的输出使用管道或进行重定向。这可以通过在done命令之后添加一个 > 处理命令来实现:

    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

shell会将for命令的结果重定向至文件output.txt,而不再显示在屏幕上。!!!!

同样适用于将循环的结果传输到另一个命令:
    $ cat test24
        #!/bin/bash
        for state in "North Dakota" Connecticut Illinois Alabama Tennessee
        do
           echo "$state is the next place to go"
        done | sort
        echo "This completes our travels"
    $ ./test24
        Alabama is the next place to go
        Connecticut is the next place to go
        Illinois is the next place to go
        North Dakota is the next place to go
        Tennessee is the next place to go
        This completes our travels

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值