美文网首页
《快乐的linux命令行》-笔记2 if/while/for

《快乐的linux命令行》-笔记2 if/while/for

作者: raincoffee | 来源:发表于2018-08-06 09:52 被阅读56次

    编写Shell

    1. 流程控制:if 分支结构

    export PATH=~/bin:"$PATH"
    

    vim 文本编辑器有许多许多的配置设置。有几个常见的选项能够有助于脚本书写:

    • :set syntax=sh 打开语法高亮
    • :set hlsearch 显示高亮查找结果
    • :set tabstop=4 设置一个tab字符所占的列数

    变量

    title="system sss"
    $title //来使用变量
    ${title} //来使用变量
    
    #!/bin/bash
    # Program to output a system information page
    TITLE="System Information Report For $HOSTNAME"
    CURRENT_TIME=$(date +"%x %r %Z")
    TIME_STAMP="Generated $CURRENT_TIME, by $USER"
    cat << _EOF_
    <HTML>
             <HEAD>
                    <TITLE>$TITLE</TITLE>
             </HEAD>
             <BODY>
                    <H1>$TITLE</H1>
                    <P>$TIME_STAMP</P>
             </BODY>
    </HTML>
    _EOF_
    

    shell 函数

    两种表现形式

    function name {
        commands
        return
    }
    
    name () {
        commands
        return
    }
    

    局部变量

    通过在变量名之前加上单词 local,来定义局部变量。这就创建了一个只对其所在的 shell 函数起作用的变量。在这个 shell 函数之外,这个变量不再存在。

    #!/bin/bash
    # local-vars: script to demonstrate local variables
    foo=0 # global variable foo
    funct_1 () {
        local foo  # variable foo local to funct_1
        foo=1
        echo "funct_1: foo = $foo"
    }
    funct_2 () {
        local foo  # variable foo local to funct_2
        foo=2
        echo "funct_2: foo = $foo"
    }
    echo "global:  foo = $foo"
    funct_1
    echo "global: foo = $foo"
    funct_2
    echo "global: foo = $foo"
    

    流程控制:if分支结构

    #! /bin/bash
    x=6
    if [ $x = 5 ]; then
        echo "x equels 5"
    elif [ $x = 6 ]; then
        echo "x equals 6"
    else
        echo "x does not equal 5"
    fi
    

    退出状态

    当命令执行完毕后,命令(包括我们编写的脚本和 shell 函数)会给系统发送一个值,叫做退出状态。 这个值是一个 0 到 255 之间的整数,说明命令执行成功或是失败。按照惯例,一个零值说明成功,其它所有值说明失败。 Shell 提供了一个参数,我们可以用它检查退出状态。用具体实例看一下:

    [me@linuxbox ~]$ ls -d /usr/bin
    /usr/bin
    [me@linuxbox ~]$ echo $?
    0
    [me@linuxbox ~]$ ls -d /bin/usr
    ls: cannot access /bin/usr: No such file or directory
    [me@linuxbox ~]$ echo $?
    2
    

    echo $? 打印上调命令返回状态

    测试

    到目前为止,经常与 if 一块使用的命令是 test。这个 test 命令执行各种各样的检查与比较。 它有两种等价模式: test expression 或者[ expression ]

    这里的 expression 是一个表达式,其执行结果是 true 或者是 false。当表达式为真时,这个 test 命令返回一个零 退出状态,当表达式为假时,test 命令退出状态为1。

    //if_learn 是一个脚本
    LORENYPLV-MB0:shell lorenyplv$ echo $[ if_learn ]
    0
    

    文件表达式

    表达式 如果下列条件为真则返回True
    file1 -ef file2 file1 和 file2 拥有相同的索引号(通过硬链接两个文件名指向相同的文件)。
    file1 -nt file2 file1新于 file2。
    file1 -ot file2 file1早于 file2。
    -b file file 存在并且是一个块(设备)文件。
    -c file file 存在并且是一个字符(设备)文件。
    -d file file 存在并且是一个目录。
    -e file file 存在。
    -f file file 存在并且是一个普通文件。
    -g file file 存在并且设置了组 ID。
    -G file file 存在并且由有效组 ID 拥有。
    -k file file 存在并且设置了它的“sticky bit”。
    -L file file 存在并且是一个符号链接。
    -O file file 存在并且由有效用户 ID 拥有。
    -p file file 存在并且是一个命名管道。
    -r file file 存在并且可读(有效用户有可读权限)。
    -s file file 存在且其长度大于零。
    -S file file 存在且是一个网络 socket。
    -t fd fd 是一个定向到终端/从终端定向的文件描述符 。 这可以被用来决定是否重定向了标准输入/输出错误。
    -u file file 存在并且设置了 setuid 位。
    -w file file 存在并且可写(有效用户拥有可写权限)。
    -x file file 存在并且可执行(有效用户有执行/搜索权限)。
    #!/bin/bash
    # test-file: Evaluate the status of a file
    FILE=~/.bashrc
    if [ -e "$FILE" ]; then
        if [ -f "$FILE" ]; then
            echo "$FILE is a regular file."
        fi
        if [ -d "$FILE" ]; then
            echo "$FILE is a directory."
        fi
        if [ -r "$FILE" ]; then
            echo "$FILE is readable."
        fi
        if [ -w "$FILE" ]; then
            echo "$FILE is writable."
        fi
        if [ -x "$FILE" ]; then
            echo "$FILE is executable/searchable."
        fi
    else
        echo "$FILE does not exist"
        exit 1
    fi
    exit
    

    这个脚本会计算赋值给常量 FILE 的文件,并显示计算结果。对于此脚本有两点需要注意。第一个, 在表达式中参数$FILE是怎样被引用的。引号并不是必需的,但这是为了防范空参数。如果$FILE的参数展开 是一个空值,就会导致一个错误(操作符将会被解释为非空的字符串而不是操作符)。用引号把参数引起来就 确保了操作符之后总是跟随着一个字符串,即使字符串为空。第二个,注意脚本末尾的 exit 命令。 这个 exit 命令接受一个单独的,可选的参数,其成为脚本的退出状态。当不传递参数时,退出状态默认为零。 以这种方式使用 exit 命令,则允许此脚本提示失败如果 $FILE 展开成一个不存在的文件名。这个 exit 命令 出现在脚本中的最后一行,是一个当一个脚本“运行到最后”(到达文件末尾),不管怎样, 默认情况下它以退出状态零终止。

    测试字符串表达式

    表达式 如果下列条件为真则返回True
    string string 不为 null。
    -n string 字符串 string 的长度大于零。
    -z string 字符串 string 的长度为零。
    string1 = string2string1 == string2 string1 和 string2 相同。 单或双等号都可以,不过双等号更受欢迎。
    string1 != string2 string1 和 string2 不相同。
    string1 > string2 sting1 排列在 string2 之后。
    string1 < string2 string1 排列在 string2 之前。

    测试整数表达式

    表达式 如果为真...
    integer1 -eq integer2 integer1 等于 integer2。
    integer1 -ne integer2 integer1 不等于 integer2。
    integer1 -le integer2 integer1 小于或等于 integer2。
    integer1 -lt integer2 integer1 小于 integer2。
    integer1 -ge integer2 integer1 大于或等于 integer2。
    integer1 -gt integer2 integer1 大于 integer2。
    #!/bin/bash
    # test-integer: evaluate the value of an integer.
    INT=-5
    if [ -z "$INT" ]; then
        echo "INT is empty." >&2
        exit 1
    fi
    if [ $INT -eq 0 ]; then
        echo "INT is zero."
    else
        if [ $INT -lt 0 ]; then
            echo "INT is negative."
        else
            echo "INT is positive."
        fi
        if [ $((INT % 2)) -eq 0 ]; then
            echo "INT is even."
        else
            echo "INT is odd."
        fi
    fi
    

    demo

    report_home_space () {
        if [[ $(id -u) -eq 0 ]]; then
            cat <<- _EOF_
            <H2>Home Space Utilization (All Users)</H2>
            <PRE>$(du -sh /home/*)</PRE>
    _EOF_
        else
            cat <<- _EOF_
            <H2>Home Space Utilization ($USER)</H2>
            <PRE>$(du -sh $HOME)</PRE>
    _EOF_
        fi
        return
    }
    

    我们计算 id 命令的输出结果。通过带有 -u 选项的 id 命令,输出有效用户的数字用户 ID 号。 超级用户总是零,其它每个用户是一个大于零的数字。知道了这点,我们能够构建两种不同的 here 文档, 一个利用超级用户权限,另一个限制于用户拥有的家目录。

    2.读取键盘输入

    read- 从标准输入读取数值

    read [-options] [variable...]
    

    这里的 options 是下面列出的可用选项中的一个或多个,且 variable 是用来存储输入数值的一个或多个变量名。 如果没有提供变量名,shell 变量 REPLY 会包含数据行。

    #!/bin/bash
    # read-integer: evaluate the value of an integer.
    echo -n "Please enter an integer -> "
    read int
    if [[ "$int" =~ ^-?[0-9]+$ ]]; then
       if [ $int -eq 0 ]; then
           echo "$int is zero."
       else
           if [ $int -lt 0 ]; then
               echo "$int is negative."
           else
               echo "$int is positive."
           fi
           if [ $((int % 2)) -eq 0 ]; then
               echo "$int is even."
           else
               echo "$int is odd."
           fi
       fi
    else
       echo "Input value is not an integer." >&2
       exit 1
    fi
    

    read 可以给多个变量赋值

    [me@linuxbox ~]$ read-multiple
    Enter one or more values > a b c d e
    var1 = 'a'
    var2 = 'b'
    var3 = 'c'
    var4 = 'd'
    var5 = 'e'
    [me@linuxbox ~]$ read-multiple
    Enter one or more values > a
    var1 = 'a'
    var2 = ''
    var3 = ''
    var4 = ''
    var5 = ''
    [me@linuxbox ~]$ read-multiple
    Enter one or more values > a b c d e f g
    var1 = 'a'
    var2 = 'b'
    var3 = 'c'
    var4 = 'd'
    var5 = 'e f g'
    

    如果 read 命令接受到变量值数目少于期望的数字,那么额外的变量值为空,而多余的输入数据则会 被包含到最后一个变量中。如果 read 命令之后没有列出变量名,则一个 shell 变量,REPLY,将会包含 所有的输入。

    read选项

    选项 说明
    -a array 把输入赋值到数组 array 中,从索引号零开始。我们 将在第36章中讨论数组问题。
    -d delimiter 用字符串 delimiter 中的第一个字符指示输入结束,而不是一个换行符。
    -e 使用 Readline 来处理输入。这使得与命令行相同的方式编辑输入。
    -n num 读取 num 个输入字符,而不是整行。
    -p prompt 为输入显示提示信息,使用字符串 prompt。
    -r Raw mode. 不把反斜杠字符解释为转义字符。
    -s Silent mode. 不会在屏幕上显示输入的字符。当输入密码和其它确认信息的时候,这会很有帮助。
    -t seconds 超时. 几秒钟后终止输入。若输入超时,read 会返回一个非零退出状态。
    -u fd 使用文件描述符 fd 中的输入,而不是标准输入。

    3.流程控制:while/until 循环

    #!/bin/bash
    # while-count: display a series of numbers
    count=1
    while [ $count -le 5 ]; do
        echo $count
        count=$((count + 1))
    done
    echo "Finished."
    
    语法:while commands; do commands; done
    

    跳出循环

    bash 提供了两个内部命令,它们可以用来在循环内部控制程序流程。 break 命令立即终止一个循环, 且程序继续执行循环之后的语句。 continue 命令导致程序跳过循环中剩余的语句,且程序继续执行 下一次循环。

    util

    until 命令与 while 非常相似,除了当遇到一个非零退出状态的时候, while 退出循环, 而 until 不退出。一个 until 循环会继续执行直到它接受了一个退出状态零。在我们的 while-count 脚本中, 我们继续执行循环直到 count 变量的数值小于或等于5。我们可以得到相同的结果,通过在脚本中使用 until 命令:

    #!/bin/bash
    # until-count: display a series of numbers
    count=1
    until [ $count -gt 5 ]; do
        echo $count
        count=$((count + 1))
    done
    echo "Finished."
    

    2. 疑难排除

    XXXXXX

    3. 流程控制:case分支

    #!/bin/bash
    # read-menu: a menu driven system information program
    clear
    echo "
    Please Select:
    1. Display System Information
    2. Display Disk Space
    3. Display Home Space Utilization
    0. Quit
    "
    read -p "Enter selection [0-3] > "
    if [[ $REPLY =~ ^[0-3]$ ]]; then
        if [[ $REPLY == 0 ]]; then
            echo "Program terminated."
            exit
        fi
        if [[ $REPLY == 1 ]]; then
            echo "Hostname: $HOSTNAME"
            uptime
            exit
        fi
        if [[ $REPLY == 2 ]]; then
            df -h
            exit
        fi
        if [[ $REPLY == 3 ]]; then
            if [[ $(id -u) -eq 0 ]]; then
                echo "Home Space Utilization (All Users)"
                du -sh /home/*
            else
                echo "Home Space Utilization ($USER)"
                du -sh $HOME
            fi
            exit
        fi
    else
        echo "Invalid entry." >&2
        exit 1
    fi
    

    这里 case 语句使用的模式和路径展开中使用的那些是一样的。模式以一个 “)” 为终止符。这里是一些有效的模式。

    模式 描述
    a) 若单词为 “a”,则匹配
    [[:alpha:]]) 若单词是一个字母字符,则匹配
    ???) 若单词只有3个字符,则匹配
    *.txt) 若单词以 “.txt” 字符结尾,则匹配
    *) 匹配任意单词。把这个模式做为 case 命令的最后一个模式,是一个很好的做法, 可以捕捉到任意一个与先前模式不匹配的数值;也就是说,捕捉到任何可能的无效值。

    还可以使用竖线字符作为分隔符,把多个模式结合起来。这就创建了一个 “或” 条件模式。这对于处理诸如大小写字符很有用处。例如:

    #!/bin/bash
    # case-menu: a menu driven system information program
    clear
    echo "
    Please Select:
    A. Display System Information
    B. Display Disk Space
    C. Display Home Space Utilization
    Q. Quit
    "
    read -p "Enter selection [A, B, C or Q] > "
    case $REPLY in
    q|Q) echo "Program terminated."
         exit
         ;;
    a|A) echo "Hostname: $HOSTNAME"
         uptime
         ;;
    b|B) df -h
         ;;
    c|C) if [[ $(id -u) -eq 0 ]]; then
             echo "Home Space Utilization (All Users)"
             du -sh /home/*
         else
             echo "Home Space Utilization ($USER)"
             du -sh $HOME
         fi
         ;;
    *)   echo "Invalid entry" >&2
         exit 1
         ;;
    esac
    

    添加的 “;;&” 的语法允许 case 语句继续执行下一条测试,而不是简单地终止运行。

    #!/bin/bash
    # case4-2: test a character
    read -n 1 -p "Type a character > "
    echo
    case $REPLY in
       [[:upper:]])    echo "'$REPLY' is upper case." ;;&
       [[:lower:]])    echo "'$REPLY' is lower case." ;;&
       [[:alpha:]])    echo "'$REPLY' is alphabetic." ;;&
       [[:digit:]])    echo "'$REPLY' is a digit." ;;&
       [[:graph:]])    echo "'$REPLY' is a visible character." ;;&
       [[:punct:]])    echo "'$REPLY' is a punctuation symbol." ;;&
       [[:space:]])    echo "'$REPLY' is a whitespace character." ;;&
       [[:xdigit:]])   echo "'$REPLY' is a hexadecimal digit." ;;&
    esac
    
    [me@linuxbox ~]$ case4-2
    Type a character > a
    'a' is lower case.
    'a' is alphabetic.
    'a' is a visible character.
    'a' is a hexadecimal digit.
    

    4.位置参数

    不能讲得更好:还是直接看文档吧

    http://billie66.github.io/TLCL/book/chap33.html

    确定参数个数

    另外 shell 还提供了一个名为 $#,可以得到命令行参数个数的变量

    #!/bin/bash
    # posit-param: script to view command line parameters
    echo "
    Number of arguments: $#
    \$0 = $0
    \$1 = $1
    \$2 = $2
    \$3 = $3
    \$4 = $4
    \$5 = $5
    \$6 = $6
    \$7 = $7
    \$8 = $8
    \$9 = $9
    "
    [me@linuxbox ~]$ posit-param a b c d
    Number of arguments: 4
    $0 = /home/me/bin/posit-param
    $1 = a
    $2 = b
    $3 = c
    $4 = d
    $5 =
    $6 =
    $7 =
    $8 =
    $9 =
    

    每次 shift 命令执行的时候,变量 2 的值会移动到变量1 中,变量 3 的值会移动到变量2 中,依次类推。 变量 $# 的值也会相应的减1。

    #!/bin/bash
    # posit-param2: script to display all arguments
    count=1
    while [[ $# -gt 0 ]]; do
        echo "Argument $count = $1"
        count=$((count + 1))
        shift
    done
    
    [me@linuxbox ~]$ posit-param2 a b c d
    Argument 1 = a
    Argument 2 = b
    Argument 3 = c
    Argument 4 = d
    

    5. 流程控制:for循环

    for variable [in words]; do
        commands
    done
    
    test:[me@linuxbox ~]$ for i in A B C D; do echo $i; done
    

    相关文章

      网友评论

          本文标题:《快乐的linux命令行》-笔记2 if/while/for

          本文链接:https://www.haomeiwen.com/subject/bdrwvftx.html