当前位置 博文首页 > Hello, New World!:《linux命令行与shell脚本编程大全》第三版

    Hello, New World!:《linux命令行与shell脚本编程大全》第三版

    作者:[db:作者] 时间:2021-09-12 18:11

    《linux命令行与shell脚本编程大全》
    全书4部分:
    ☆ 【1】linux命令行(1-10章)
    ☆ 【2】shell脚本编程基础(11-16章)下:输入输出与脚本控制
    ☆ 【3】高级shell脚本编程(17-23章)
    ☆ 【4】创建实用的脚本(24-26章)


    >>第14章丶处理用户输入


    $n命令行参数

    bash shell会将一些称为位置参数(positional parameter)的特殊变量分配给输入到命令行中的所有参数。
    位置参数变量是标准的数字:
    $0是程序名, $1是第一个参数, $2是第二个参数,依次类推,直到第九个参数 $9
    /* 位置参数演示 */
    #!/bin/bash
    # using one command line parameter
    #
    factorial=1
    for (( number = 1; number <= $1 ; number++ ))
    do
        factorial=$[ $factorial * $number ]
    done
    echo "The factorial of $1 is $factorial."
    [~/shell/4]$: test1.sh 5
    The factorial of 5 is 120.

    如果需要输入更多的命令行参数,则每个参数都必须用 空格分开。
    shell会将每个参数分配给对应的变量,变量可以分配数字,也可以分配字符串。
    [ 注意]每个参数都是用空格分隔的,所以shell会将空格当成两个值的分隔符。要在 参数值中包含空格,必须要用引号(单引号或双引号均可)。
    /* 位置参数变量传递字符串 */
    #!/bin/bash
    # testing string parameters
    #
    echo Hello $1, glad to meet you.
    [~/shell/4]$: test3.sh "Rich Blum"
    Hello Rich Blum, glad to meet you.

    在第9个变量之后,你必须在变量数字周围加上花括号,比如 ${10}
    如:total=$[ ${10} * ${11} ]

    $0读取脚本名

    可以用 $0 参数获取shell在命令行启动的脚本名。这在编写多功能工具时很方便。
    " basename" 命令会返回不包含路径的脚本名。
    /* basename 和 $0 演示*/
    #!/bin/bash
    # Using basename with the $0 parameter
    #
    name=$(basename $0)
    echo The script name is: $name
    [~/shell/4]$: bash /home/jiangyuan/shell/4/test2.sh
    The script name is: test2.sh

    /* 不同功能的脚本 */
    #!/bin/bash
    # Testing a Multi-function script
    #
    name=$(basename $0)
    #
    if [ $name = "addem" ]
    then
        total=$[ $1 + $2 ]
    #
    elif [ $name = "multem" ]
    then
        total=$[ $1 * $2 ]
    fi
    #
    echo
    echo The calculated value is $total
    [~/shell/4]$: cp test6.sh addem
    [~/shell/4]$: chmod u+x addem
    [~/shell/4]$: ln -s test6.sh multem
    [~/shell/4]$: ./addem 2 5
    The calculated value is 7
    [~/shell/4]$: ./multem 2 5
    The calculated value is 10

    在使用参数前一定要检查其中是否存在数据。如,使用了 -n测试来检查命令行参数$1中是否有数据。
    if [ -n "$1" ]
    then
    echo Hello $1, glad to meet you.
    else
    echo "Sorry, you did not identify yourself. "
    fi

    $#特殊参数变量

    特殊变量 $#含有脚本运行时携带的命令行参数的个数。
    echo There were $# parameters supplied.
    $ ./test8.sh 1 2 3 4 5 6 7 8 9 10
    There were 10 parameters supplied.
    如:测试参数总数是否等于2,如果不等于2,使用 if [ $# -ne 2 ]

    ${!#} 表示最后一个命令行参数的值。原型为:${$#},此种写法会报错,故使用!#表示最后一个参数。

    $*和$@抓取所有数据

    $*和$@变量可以用来轻松访问所有的参数。这两个变量都能够在单个变量中存储所有的命令行参数。
    $*变量会将命令行上提供的所有参数当作一个单词保存。
    $@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。
    /* $* 和 $@ */
    #!/bin/bash
    # testing $* and $@
    #
    count=1
    #
    for param in "$*"
    do
        echo "\$* Parameter #$count = $param"
        count=$[ $count + 1 ]
    done
    #
    count=1
    #
    for param in "$@"
    do
        echo "\$@ Parameter #$count = $param"
        count=$[ $count + 1 ]
    done
    [~/shell/4]$: test3.sh 1 2 3 4
    $* Parameter #1 = 1 2 3 4
    $@ Parameter #1 = 1
    $@ Parameter #2 = 2
    $@ Parameter #3 = 3
    $@ Parameter #4 = 4

    shift移动变量

    shift命令会根据它们的相对位置来移动命令行参数。
    在使用shift命令时,默认情况下它会将每个参数变量向左移动一个位置。所以,变量$3的值会移到$2中,变量$2的值会移到$1中,而变量$1的值则会被删除(注意,变量$0的值,也就是程序名,不会改变)。
    /* shift命令演示 */
    #!/bin/bash
    # test shift command
    count=1
    while [ -n "$1" ]
    do
        echo "Param #$count=$1"
        count=$[ $count + 1 ]
        shift
    done
    [~/shell/4]$: test4.sh hello world how are you
    Param #1=hello
    Param #2=world
    Param #3=how
    Param #4=are
    Param #5=you
    [ 注意]使用shift命令的时候要小心。如果某个参数被移出,它的值就被丢弃了,无法再恢复。

    一次移动多个位置,使用 shift n,移动n个位置,如:
    echo "The original parameters: $*"
    shift 2
    echo "Here's the new first parameter: $1"

    $: ./test14.sh 1 2 3 4 5
    The original parameters: 1 2 3 4 5
    Here's the new first parameter: 3

    查找选项:
    提取每个单独参数时,使用case判断是否为选项:
    while [ -n "$1" ]
    do
        case "$1" in
            -a) echo "Found the -a option" ;;
            -b) echo "Found the -b option" ;;
            -c) echo "Found the -c option" ;;
            *) echo "$1 is not an option" ;;
        esac
        shift
    done

    --区分选项和参数

    shell脚本中同时使用选项和参数时,Linux中处理这个问题的标准方式是用特殊字符双破折线(--)来将二者分开,该字符会告诉脚本何时选项结束以及普通参数何时开始。
    要检查双破折线,只要在case语句中加一项就行了。
    /* --双破折线演示 */
    #!/bin/bash
    # extracting options and parameters
    while [ -n "$1" ]
    do
        case "$1" in
            -a) echo "found the -a option";;
            -b) echo "found the -b option";;
            -c) echo "found the -c option";;
            --) shift
                break;;
             *) echo "$1 is not an option";;
        esac
        shift
    done
    #
    count=1
    for param in $@
    do
        echo "param #$count: $param"
        count=$[ $count + 1 ]
    done
    [~/shell/4]$: test5.sh -a -a -b test1.sh test2.sh test3.sh
    found the -a option
    found the -a option
    found the -b option
    test1.sh is not an option
    test2.sh is not an option
    test3.sh is not an option
    [~/shell/4]$: test5.sh -a -a -b -- test1.sh test2.sh test3.sh
    found the -a option
    found the -a option
    found the -b option
    param #1: test1.sh
    param #2: test2.sh
    param #3: test3.sh

    处理带值的选项:
    -b) param="$2"
    echo "Found the -b option, with parameter value $param"
    shift ;;
    $: ./test17.sh -a -b test1 -d
    Found the -a option
    Found the -b option, with parameter value test1
    -d is not an option

    getopt转换选项和参数格式

    命令可以接受一系列任意形式的命令行选项和参数,并自动将它们转换成适当的格式。它的命令格式如下:
    ? ? getopt optstring parameters
    在optstring中列出你要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后加一个冒号。 getopt命令会基于你定义的optstring解析提供的参数。
    $: getopt ab:cd -a -b test1 -cd test2 test3
    -a -b test1 -c -d -- test2 test3
    在脚本中使用getopt来格式化脚本所携带的任何命令行选项或参数。方法是用getopt命令生成的格式化后的版本来替换已有的命令行选项和参数。用set命令能够做到。
    set命令的选项之一是双破折线( --),它会将命令行参数替换成set命令的命令行值。
    ? ? $: set -- $(getopt -q ab:cd "$@")
    /* getopt 脚本演示 */
    #!/bin/bash
    # extracting options and parameters
    set -- $(getopt -q ab:cd "$@")
    while [ -n "$1" ]
    do
        case "$1" in
            -a) echo "found the -a option";;
            -b) param="$2"
                echo "found the -b option, with param value $param"
                shift;;
            -c) echo "found the -c option";;
            --) shift
                break;;
             *) echo "$1 is not an option";;
        esac
        shift
    done
    #
    count=1
    for param in $@
    do
        echo "param #$count: $param"
        count=$[ $count + 1 ]
    done
    [~/shell/4]$: test6.sh -a -b test1 -cd test2 test3 test4
    found the -a option
    found the -b option, with param value 'test1'
    found the -c option
    -d is not an option
    param #1: 'test2'
    param #2: 'test3'
    param #3: 'test4'

    getopts转换选项和参数格式支持含空格

    每次调用它时,它一次只处理命令行上检测到的一个参数。处理完所有的参数后,它会退出并返回一个大于0的退出状态码。这让它非常适合用解析命令行所有参数的循环中。getopts命令的格式如下:
    ? ? getopts optstring variable
    /* getopts 脚本演示 */
    while getopts :ab:c opt
    do
    case "$opt" in
    a) echo "Found the -a option" ;;
    b) echo "Found the -b option, with value $OPTARG";;
    c) echo "Found the -c option" ;;
    *) echo "Unknown option: $opt";;
    esac
    done
    $: ./test19.sh -b "test1 test2" -a
    Found the -b option, with value test1 test2
    Found the -a option

    在getopts处理每个选项时,它会将OPTIND环境变量值增一。在getopts完成处理时,你可以使用shift命令和OPTIND值来移动参数:shift $[ $OPTIND - 1 ]

    脚本中常用linux命令选项:
    -a 显示所有对象
    -c 生成一个计数
    -d 指定一个目录
    -e 扩展一个对象
    -f 指定读入数据的文件
    -h 显示命令的帮助信息
    -i 忽略文本大小写
    -l 产生输出的长格式版本
    -n 使用非交互模式(批处理)
    -o 将所有输出重定向到的指定的输出文件
    -q 以安静模式运行
    -r 递归地处理目录和文件
    -s 以安静模式运行
    -v 生成详细输出
    -x 排除某个对象
    -y 对所有问题回答yes

    read获得用户输入

    read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后, read命令会将数据放进一个变量。
    /* read 演示 */
    echo -n "Enter your name: "    # -n 可以去除echo输出行的最后一个\n符号
    read name
    echo "Hello $name, welcome to my program. "
    
    read -p "Please enter your age: " age
    days=$[ $age * 365 ]
    echo "That makes you over $days days old! "
    [~/shell/4]$: test7.sh
    Please enter your age:27
    That makes you over 9855 days old!

    也可以在read命令行中不指定变量。如果是这样, read命令会将它收到的任何数据都放进特殊环境变量REPLY中,对其取值即可 $REPLY
    -t选项指定了read命令等待输入的秒数。当计时器过期后, read命令会返回一个非零退出状态码。
    如:if read -t 5 -p "Please enter your name: " name
    也可以不对输入过程计时,而是让read命令来统计输入的字符数。当输入的字符达到预设的字符数时,就自动退出,将输入的数据赋给变量。
    /* read 统计输入字符数演示 */
    #!/bin/bash
    # getting just one character of input
    #
    read -n2 -p "Do you want to continue [Y/n]? " answer
    case $answer in
    Y | y) echo
           echo "fine, continue on…";;
    N | n) echo
           echo "OK, goodbye…"
           exit;;
        *) echo
           echo "fine, continue on…";;
    esac
    echo "This is the end of the script."
    [~/shell/4]$: test8.sh
    Do you want to continue [Y/n]? Y
    fine, continue on…
    This is the end of the script.
    [~/shell/4]$: test8.sh
    Do you want to continue [Y/n]?
    fine, continue on…
    This is the end of the script.
    [~/shell/4]$: test8.sh
    Do you want to continue [Y/n]? n
    OK, goodbye…
    // 本例中-n2可以接收read的2个字符,如Y\n,或者一个\n选项的话效果一样。

    -s选项可以避免在read命令中输入的数据出现在显示器上(实际上,数据会被显示,只是read命令会将文本颜色设成跟背景色一样)。
    如:read -s -p "Enter your password: " pass
    read命令来读取Linux系统上文件里保存的数据。每次调用read命令,它都会从文件中读取一行文本。当文件中再没有内容时, read命令会退出并返回非零退出状态码。
    /* read 读取文件内容 */
    #!/bin/bash
    # test read file
    count=1
    cat $1 | while read line
    do
        echo "Line $count: $line"
        count=$[ $count + 1 ]
    done
    echo "Finished processing the $1."
    
    [~/shell/4]$: test10.sh test10.sh
    Line 1: #!/bin/bash
    Line 2: # test read file
    Line 3: count=1
    Line 4: cat $1 | while read line
    Line 5: do
    Line 6: echo "Line $count: $line"
    Line 7: count=$[ $count + 1 ]
    Line 8: done
    Line 9: echo "Finished processing the $1."
    Finished processing the test10.sh.

    >>第15章丶呈现数据


    bash shell保留了前三个文件描述符0、 1、2,分别对应STDIN(<)、STDOUT(>)、STDERR。
    cat 单独输入命令行,shell会从STDIN输入。输入一行, cat命令就会显示出一行。
    如:cat < testfile 会将testfile的内容重定向输入到cat命令对应的STDIN中。输入追加符号:<<
    如:ls -l > test2 会将ls -l的内容重定向输出到test2文件中。输出追加符号:>>

    重定向错误:
    $: ls -al badfile 2> test4
    $: cat test4
    ls: cannot access badfile: No such file or directory

    重定向错误和数据:
    $: ls -al test test2 test3 badtest 2> test6 1> test7
    $: cat test6
    ls: cannot access test: No such file or directory
    ls: cannot access badtest: No such file or directory
    $: cat test7
    -rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2
    -rw-rw-r-- 1 rich rich 0 2014-10-16 11:33 test3

    &>将标准错误和输出重定向到同一文件
    将STDERR和STDOUT的输出重定向到同一个输出文件。为此bash shell提供了特殊的重定向符号&>。
    $: ls -al test test2 test3 badtest &> test7
    $: cat test7
    ls: cannot access test: No such file or directory
    ls: cannot access badtest: No such file or directory
    -rw-rw-r-- 1 rich rich 158 2014-10-16 11:32 test2
    -rw-rw-r-- 1 rich rich 0 2014-10-16 11:33 test3

    >&临时重定向:
    如果有意在脚本中生成错误消息,可以将单独的一行输出重定向到STDERR。格式为:>&stdfileno
    $: echo "This is an error message" >&2
    默认情况下, Linux会将STDERR导向STDOUT。但是,如果你在运行脚本时重定向了STDERR,脚本中所有导向STDERR的文本都会被重定向。
    /* 临时重定向演示 */
    $: cat test8
    #!/bin/bash
    # testing STDERR messages
    echo "This is an error" >&2
    echo "This is normal output"
    $: ./test8
    This is an error
    This is normal output
    $: ./test8 2> test9
    This is normal output
    $: cat test9
    This is an error

    [ 窍门]通过STDOUT显示的文本显示在了屏幕上,而发送给STDERR的echo语句的文本则被重定向到了输出文件。这个方法非常适合在脚本中生成错误消息。

    exec永久重定向

    如果脚本中有大量数据需要重定向,那重定向每个echo语句就会很烦琐。取而代之,你可以用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符。
    /* exec永久重定向演示 */
    #!/bin/bash
    # redirecting all output to a file
    exec 1>$1
    echo "This is a test of redirecting all output"
    echo "from a script to another file."
    echo "without having to redirect every individual line"
    $ ./test10 testfile
    $ cat testfile
    This is a test of redirecting all output
    from a script to another file.
    without having to redirect every individual line

    在脚本中重定向输入:
    exec命令允许你将STDIN重定向到Linux系统上的文件中:
    ? ? exec 0< testfile
    这个命令会告诉shell它应该从文件testfile中获得输入,而不是STDIN。
    这是在脚本中从待处理的文件中读取数据的绝妙办法。 Linux系统管理员的一项日常任务就是从日志文件中读取数据并处理。

    创建输出文件描述符:
    用exec命令来给输出分配文件描述符。和标准的文件描述符一样,一旦将另一个文件描述符分配给一个文件,这个重定向就会一直有效,直到你重新分配。
    /* 演示 */
    #!/bin/bash
    # using an alternative file descriptor
    exec 3>test13out
    # exec 3>>test13out2    追加到文件中去
    echo "This should display on the monitor"
    echo "and this should be stored in the file" >&3
    echo "Then this should be back on the monitor"
    $: ./test13
    This should display on the monitor
    Then this should be back on the monitor
    $: cat test13out
    and this should be stored in the file
    // exec 3>filename ? →→→ ? echo "some string" >&3 即可将单独的内容输入到文件filename中

    创建输入文件描述符:
    /* 演示 */
    #!/bin/bash
    # redirecting input file descriptors
    touch testfile
    exec 6<&0
    exec 0< testfile
    count=1
    while read line
    do
        echo "Line #$count: $line"
        count=$[ $count + 1 ]
    done
    exec 0<&6
    read -n2 -p "Are you done now? [Y/n]" answer
    case $answer in
        Y|y) echo "Goodbye";;
        N|n) echo "Sorry, this is the end.";;
          *) echo "Goodbye";;
    esac
    // 文件描述符6用来保存STDIN的位置。然后脚本将STDIN重定向到一个文件。read命令的所有输入都来自重定向后的STDIN(也就是输入文件)。在读取了所有行之后,脚本会将STDIN重定向到文件描述符6,从而将STDIN恢复到原先的位置。该脚本用了另外一个read命令来测试STDIN是否恢复正常了。这次它会等待键盘的输入。
    $: test15.sh
    Are you done now? [Y/n]Y
    Goodbye

    创建读写文件描述符:
    exec 3<> testfile
    read line <&3


    关闭文件描述符:
    exec 3>&-
    /* 演示 */
    #!/bin/bash
    # testing closing file descriptors
    exec 3> test17file
    echo "This is a test line of data" >&3
    exec 3>&-
    echo "This won't work" >&3
    $: test17.sh
    ./test17.sh: 行 6: 3: 错误的文件描述符

    lsof

    会列出整个Linux系统打开的所有文件描述符。lsof命令位于/usr/bin目录。
    最常用的有 -p-d,前者允许指定进程ID(PID),后者允许指定要显示的文件描述符编号。
    要想知道进程的当前PID,可以用特殊环境变量$$(shell会将它设为当前PID)。 -a选项用来对其他两个选项的结果执行布尔AND运算,这会产生如下输出。
    $:' lsof -a -p $$ -d 0,1,2
    COMMAND ?PID ? ? ?USER ? FD ? TYPE DEVICE SIZE/OFF NODE NAME
    bash ? ?2447 jiangyuan ? ?0u ? CHR ?136,0 ? ? ?0t0 ? ?3 /dev/pts/0
    bash ? ?2447 jiangyuan ? ?1u ? CHR ?136,0 ? ? ?0t0 ? ?3 /dev/pts/0
    bash ? ?2447 jiangyuan ? ?2u ? CHR ?136,0 ? ? ?0t0 ? ?3 /dev/pts/0
    /* 演示多个替代性文件描述符 */
    #!/bin/bash
    # testing lsof with file descriptors
    exec 3> test18file1
    exec 6> test18file2
    exec 7< testfile
    /usr/bin/lsof -a -p $$ -d0,1,2,3,6,7
    $: test18.sh
    COMMAND ? ?PID ? ? ?USER ? FD ? TYPE DEVICE SIZE/OFF ? NODE NAME
    test18.sh 3162 jiangyuan ? ?0u ? CHR ?136,0 ? ? ?0t0 ? ? ?3 /dev/pts/0
    test18.sh 3162 jiangyuan ? ?1u ? CHR ?136,0 ? ? ?0t0 ? ? ?3 /dev/pts/0
    test18.sh 3162 jiangyuan ? ?2u ? CHR ?136,0 ? ? ?0t0 ? ? ?3 /dev/pts/0
    test18.sh 3162 jiangyuan ? ?3w ? REG ? ?8,1 ? ? ? ?0 397905 /home/jiangyuan/shell/4/test18file1
    test18.sh 3162 jiangyuan ? ?6w ? REG ? ?8,1 ? ? ? ?0 397921 /home/jiangyuan/shell/4/test18file2
    test18.sh 3162 jiangyuan ? ?7r ? REG ? ?8,1 ? ? ? 55 397712 /home/jiangyuan/shell/4/testfile

    /dev/null

    阻止命令输出,将STDERR重定向到一个叫作null文件的特殊文件。shell输出到null文件的任何数据都不会保存,全部都被丢掉了。
    你重定向到该位置的任何数据都会被丢掉,不会显示。
    这是避免出现错误消息,也无需保存它们的一个常用方法。
    $: ls -al > /dev/null
    $: ls -al badfile test16 2> /dev/null
    也可以在输入重定向中将/dev/null作为输入文件。
    $: cat /dev/null > testfile
    [ 窍门] 这是清除日志文件的一个常用方法,因为日志文件必须时刻准备等待应用程序操作。

    mktemp

    创建临时文件到 /tmp目录下创建一个唯一的临时文件。 shell会创建这个文件,但不用默认的umask值。
    一旦创建了文件,你就在脚本中有了完整的读写权限,但其他人没法访问它(当然, root用户除外)。
    要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名, 在文件名末尾加上6个X就行了
    mktemp命令会用6个字符码替换这6个X,从而保证文件名在目录中是唯一的。
    $: mktemp testing.XXXXXX
    testing.97qCFL
    $: mktemp testing.XXXXXX
    testing.2TQqpp
    /* 演示 */
    #!/bin/bash
    # creating and using a temp file
    tempfile=$(mktemp test19.XXXXXX)
    exec 3>$tempfile
    echo "This script writes to temp file $tempfile"
    echo "This is the first line" >&3
    echo "This is the second line." >&3
    echo "This is the last line." >&3
    exec 3>&-
    echo "Done creating temp file. The contents are:"
    cat $tempfile
    rm -f $tempfile 2> /dev/null
    $: test19.sh
    This script writes to temp file test19.IwhZwI
    Done creating temp file. The contents are:
    This is the first line
    This is the second line.
    This is the last line.

    在/tmp 目录创建临时文件
    -t选项会强制mktemp命令来在系统的临时目录来创建该文件。
    $: mktemp -t tmp.XXXXXX
    /tmp/tmp.MwA0wt

    在/tmp 目录创建临时目录
    -d选项告诉mktemp命令来创建一个临时目录而不是临时文件。
    $: mktemp -d dir.XXXXXX
    /tmp/dir.OAeZrU/

    tee 记录消息

    将输出同时发送到显示器和日志文件,相当于管道的一个T型接头。它将从STDIN过来的数据同时发往两处,一处是STDOUT,另一处是tee命令行所指定的文件名。
    格式: tee filename

    默认情况下, tee命令会在每次使用时覆盖输出文件内容。
    $: date | tee testfile
    Sun Jun 18 17:37:27 CST 2017
    $: cat testfile
    Sun Jun 18 17:37:27 CST 2017
    将数据追加到文件中,必须用-a选项。

    /* 重定向实例(操作.csv文件到SQL数据库) */
    #!/bin/bash
    # read file and create INSERT statements for MySQL
    outfile='members.sql'
    IFS=','
    while read lname fname address city state zip
    do
        cat >> $outfile << EOF
        INSERT INTO members (lname,fname,address,city,state,zip) VALUES
        ('$lname', '$fname', '$address', '$city', '$state', '$zip');
    EOF
    done < ${1}
    $: cat members.csv
    Blum,Richard,123 Main St.,Chicago,IL,60601
    Blum,Barbara,123 Main St.,Chicago,IL,60601
    Bresnahan,Christine,456 Oak Ave.,Columbus,OH,43201
    Bresnahan,Timothy,456 Oak Ave.,Columbus,OH,43201
    $: ./test23 < members.csv
    $: cat members.sql
    INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Blum',
    'Richard', '123 Main St.', 'Chicago', 'IL', '60601');
    INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Blum',
    'Barbara', '123 Main St.', 'Chicago', 'IL', '60601');
    INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Bresnahan',
    'Christine', '456 Oak Ave.', 'Columbus', 'OH', '43201');
    INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Bresnahan',
    'Timothy', '456 Oak Ave.', 'Columbus', 'OH', '43201');

    >>第16章丶控制脚本


    处理信号:
    Linux系统和应用程序可以生成超过30个信号。最常见的信号:
    1 ?SIGHUP ?挂起进程
    2 ?SIGINT ?终止进程
    3 ?SIGQUIT 停止进程
    9 ?SIGKILL 无条件终止进程
    15 SIGTERM 尽可能终止进程
    17 SIGSTOP 无条件停止进程,但不是终止进程
    18 SIGTSTP 停止或暂停进程,但不终止进程
    19 SIGCONT 继续运行停止的进程
    默认情况下, bash shell会忽略收到的任何SIGQUIT (3)和SIGTERM (5)信号(正因为这样,交互式shell才不会被意外终止)。但是bash shell会处理收到的SIGHUP (1)和SIGINT (2)信号。
    中断进程:Ctrl+C组合键会生成SIGINT信号。
    暂停进程:Ctrl+Z组合键会生成一个SIGTSTP信号,停止shell中运行的任何进程。

    trap捕获信号

    trap命令允许你来指定shell脚本要监看并从shell中拦截的Linux信号。如果脚本收到了trap命令中列出的信号,该信号不再由shell处理,而是交由本地处理。trap命令的格式是:
    ? ? trap commands signals
    /* trap命令演示 */
    #!/bin/bash
    # Testing signal trapping
    trap "echo 'Sorry! I have trapped Ctrl+C'" SIGINT
    echo This is a test script
    count=1
    while [ $count -le 10 ]
    do
        echo "Loop #$count"
        sleep 1
        count=$[ $count + 1 ] 
    done
    $: test1.sh
    This is a test script
    Loop #1
    Loop #2
    ^CSorry! I have trapped Ctrl+C
    Loop #3
    ^CSorry! I have trapped Ctrl+C
    Loop #4
    Loop #5
    ^CSorry! I have trapped Ctrl+C
    Loop #6
    Loop #7
    Loop #8
    Loop #9
    Loop #10

    捕获脚本退出:
    要捕获shell脚本的退出,只要在trap命令后加上EXIT信号就行。
    /* 演示 */
    #!/bin/bash
    # Trapping the script exit
    #
    trap "echo Goodbye..." EXIT
    #
    count=1
    while [ $count -le 5 ]
    do
        echo "Loop #$count"
        sleep 1
        count=$[ $count + 1 ]
    done
    $: test2.sh
    Loop #1
    Loop #2
    Loop #3
    ^CGoodbye...

    修改或移除捕获:
    在脚本中的不同位置进行不同的捕获处理,只需重新使用带有新选项的trap命令。
    只需要在trap命令与希望恢复默认行为的信号列表之间加上两个破折号--就行了。
    # Remove the trap
    trap -- SIGINT
    echo "I just removed the trap"

    [ 窍门] 也可以在trap命令后使用单破折号来恢复信号的默认行为。单破折号和双破折号都可以正常发挥作用。
    移除信号捕获后,脚本按照默认行为来处理SIGINT信号,也就是终止脚本运行。

    以后台模式运行脚本:
    只要在命令后加个 &符就行了。
    $: ./test4.sh &
    [1] 3327
    // [1] 作业号;3327 进程唯一pid

    运行多个后台作业:
    每次启动新作业时, Linux系统都会为其分配一个新的作业号和PID。通过ps命令,可以看到所有脚本处于运行状态。

    在非控制台下运行脚本:
    nohup

    nohup命令运行了另外一个命令来阻断所有发送给该进程的SIGHUP信号。这会在退出终端会话时阻止进程退出。
    $: nohup ./test1.sh &
    nohup命令会自动将STDOUT和STDERR的消息重定向到一个名为 nohup.out的文件中。
    在进程完成运行后,你可以查看nohup.out文件中的输出结果。

    作业控制:
    jobs

    jobs命令允许查看shell当前正在处理的作业。
    $: test4.sh > test4.out &
    [1] 3360
    $: ps -l
    F S ? UID ? PID ?PPID ?C PRI ?NI ADDR SZ WCHAN ?TTY ? ? ? ? ?TIME CMD
    0 S ?1001 ?2447 ?2446 ?0 ?80 ? 0 - ?3259 wait ? pts/0 ? ?00:00:01 bash
    0 S ?1001 ?3360 ?2447 ?0 ?80 ? 0 - ?1651 wait ? pts/0 ? ?00:00:00 test4.sh
    0 S ?1001 ?3361 ?3360 ?0 ?80 ? 0 - ?1403 hrtime pts/0 ? ?00:00:00 sleep
    下一篇:没有了