• UNIX/Linux系统管理技术手册(2)----bash脚本编程


    1. 一个简单的例子:

    $ vim readname.sh
    #file:readname.sh
    #!/bin/bash echo -n "Enter your name: " read user_name if [ -n "$user_name" ] ; then echo "Hello $user_name!" exit 0 else echo "You did not tell me your name..." exit 1 fi

    执行:

    -rw-r--r--. 1 root root 176 7月  19 12:13 readname.sh
    root@javis:~/Documents/bash$ chmod +x readname.sh
    root@javis:~/Documents/bash$ ./readname.sh
    Enter your name: Julian
    Hello Julian!
    root@javis:~/Documents/bash$ ./readname.sh
    Enter your name: 
    You did not tell me your name...

    2. 命令行参数和函数

      给一个脚本的命令行参数可以成为变量,这些变量的名字是数字。$1 是第一个命令行的参数,$2 是第二个,以此类推。$0 是调用该脚本所采用的名字,所以它的取值并不固定。

      变量$# 是提供给脚本的命令行参数的个数,变量$*里保存有全部的参数。这两个变量都不包括或者算上 $0

      如果调用的脚本不带参数,或者参数不正确,那么该脚本应该打印一段用法说明,提醒用户怎样使用它。下面这个脚本的例子接受两个参数,验证这两个参数都是目录,然后显示它们。如果参数无效,那么这个脚本会打印一则用法说明,并且用一个非零的返回码退出。如果调用这个脚本的程序检查该返回码,那么它就会知道这个脚本有没有正确执行。

    # file-name: src_dst.sh
    #!/bin/bash function show_usage { echo "Usage: $0 source_dir dest_dir" exit 1 } # Main program starts here if [ $# -ne 2 ]; then show_usage else # There are two arguments if [ -d $1 ]; then source_dir=$1 else echo 'Invalid source directory' show_usage fi if [ -d $2 ]; then dest_dir=$2 else echo 'Invalid destination directory' show_usage fi fi printf "Source directory is ${source_dir} " printf "Destination directory is ${dest_dir} "

    正确使用:

    root@javis:~/Documents/bash$ ./src_dst.sh  /bin /etc
    Source directory is /bin
    Destination directory is /etc
    root@javis:~/Documents/bash$ 

    错误使用:

    root@javis:~/Documents/bash$ ./src_dst.sh  hello hi
    Invalid source directory
    Usage: %0 source_dir dest_dir

    3. 变量的作用域

      在脚本里的变量是全局变量,但是函数可以用 local 声明语句,创建自己的局部变量。考虑下面的代码:

    #!/bin/bash
    function localizer {
            echo "==> In function localizer, a starts as '$a'"
            local a
            echo "==> After local declaration , a is '$a'"
            a="localizer version"
            echo "==> Leaving localizer, a is '$a'"
    }
    
    a="test"
    echo "Before calling localizer, a is '$a'"
    localizer
    echo "After calling localizer, a is '$a'"

    运行结果:

    root@javis:~/Documents/bash$ ./test.sh
    Before calling localizer, a is 'test'
    ==> In function localizer, a starts as 'test'
    ==> After local declaration , a is ''
    ==> Leaving localizer, a is 'localizer version'
    After calling localizer, a is 'test'

      以上结果显示,局部变量 $a 屏蔽了全局变量 $a。在localizer内,在碰到 local 声明了局部变量 $a 之前,全局变量 $a 都可以可见;local 实际上是一条命令,它从执行的地方开始,创建局部变量。

    4.控制流程

    (i)  一条 if 语句的结束标识是 fi 。要把几条 if 语句串起来,可以用 elif 这个关键字,它的意思是 "else if"。例如:

    if [ $base -eq 1 ] && [ $dm -eq 1 ]; then
        doSomeThingA
    elif [ $base -ne 1 ] && [ $dm -eq 1 ]; then
        doSomeThingB
    else
        echo '==> Doing Nothing'
    fi

    下表给出 bash 的数值和字符串比较运算。 bash 比较数值采用文字运算符,而比较字符串采用符号运算符,这正好和 Perl 相反。

    字符串 数值 为真,如果
    x=y x -eq y  x等于y
    x!=y x -ne y x不等于y
    x<y x -lt y x小于y
    x<=y x -le y x小于等于y
    x>y x -gt y x大于y
    x>=y x -ge y x 大于等于y
    -n x - x不为空
    -z x - x为空

    下表给出 bash 的文件取值运算符

    运算符 为真,如果
    -d file file 存在,且是目录
    -e file file 存在
    -f file file 存在,且是普通文件
    -r file 用户有 file 的读权限
    -s file file 文件存在且不为空
    -w file

    用户有 file 的写权限

    file1 -nt file2 file1 比 file2 新
    file1 -ot file2 file1 比 file2 旧

    (ii)  虽然 elif 的形式能用,但是为了清除起见,用 case 语句做选择是更好的方法。 case 的语法如下面的这个函数历程所示,该函数集中给一个脚本写日志。特别值得注意的是,每一选择条件之后有一个右括号,而在条件符合时每个要执行的语句块之后有两个分号。case 语句以 esac 结尾。

    # The log level is set in the global variable LOG_LEVEL. The choices
    # are , from most to least severe, Error, Warning, Info , and Debug.
    
    function logMsg {
            message_level=$1
            message_itself=$2
            LOG_LEVEL=11
            if [ $message_level -le $LOG_LEVEL ]; then
                    case $message_level in
                            0) message_level_text="Error";;
                            1) message_level_text="Warning";;
                            2) message_level_text="Info";;
                            3) message_level_text="Debug";;
                            *) message_level_text="Other";;
                    esac
                    echo "${message_level_text}: $message_itself"
            fi
    }
    
    logMsg 1 testing
    
    ~                          

    测试输出:

    root@javis:~/Documents/bash$ ./test1.sh
    Warning: testing

    这个函数演示了许多系统管理应用经常采取的 "日志级别"方案。脚本的代码产生详尽程度不同的日志消息,但是只有那些在全局设定的阈值 $LOG_LEVEL 之内的消息才被真正记录到日志里,或者采取相应的行动。为了阐明每则消息的重要性,在消息文字之前用一个标签说明其关联的日志级别。

    (iii) 循环

    ① bash 的 for...in 结构可以让它很容易对一组值或者文件执行若干操作,尤其是和文件名通配功能联合起来使用的时候。在下面这个 for 循环里,其中的*.sh 模式会返回当前目录下能够匹配的文件名列表。 for 语句则逐一遍历这个列表,接着把每个文件名赋值给变量 $script。

    #!/bin/bash
    suffix=BACKUP--`date +%Y%m%d-%H%M`
    
    for script in *.sh; do
            newname="$script.$suffix"
            echo "Copying $script to $newname"
            cp $script $newname
    done

    运行测试:

    root@javis:~/Documents/bash$ ./loop.sh
    Copying loop.sh to loop.sh.BACKUP--20160719-1612
    Copying readname.sh to readname.sh.BACKUP--20160719-1612
    Copying src_dst.sh to src_dst.sh.BACKUP--20160719-1612
    Copying test1.sh to test1.sh.BACKUP--20160719-1612
    Copying test.sh to test.sh.BACKUP--20160719-1612

    也可以静态的输入文件名,就像下面这样:

    for script  file1.sh  file2.sh ; do

    ② bash 也有从传统语言来看更为熟悉的 for 循环,在这种 for 循环里,可以指定起始、增量和终止子句。例如:

    for ((i=0;i<$CPU_COUNT; i++)); do
        CPU_LIST="$CPU_LIST $i"
    done

     ③ 接下来的例子演示了 bash 的while 循环,这种循环也能用于处理命令行参数,以及读取一个文件里的各行:

    #!/bin/bash
    exec 0<$1
    counter=1
    while read line;do
            echo "$counter: $line"
            ((counter++))
    done

    运行实例:

    root@javis:~/Documents/bash$ ./whileloop.sh poem.txt
    1: good , better , best
    2: never let it rest
    3: till good is better
    4: and better is best

      上面这段脚本有两个有趣的功能。exec语句重新定义了该脚本的标准输入,变成由第一个命令行参数指定的任何文件。这个文件必须要有,否则脚本会出错。

      在 while 子句里的 read 语句实际上是 shell 的内置命令,但它的作用就和一条外部命令一样。外部命令也可以放在 while 子句里;在这种情况下,当外部命令返回一个非零退出状态时,它会结束 while 循环。

      表达式 $((counter++)) 这样的 $((...)) 写法要求强制进行数值计算。它还可以利用$来标记变量名。++是人们在C 和其他语言中熟悉的后置递增运算符。它返回它前面的那个变量的值,但返回之后还要把这个变量的值再加1.

      $((...))的技巧在双引号里也起作用,所以可以把整个循环体紧凑地写到一行里。

    while read line; do
        echo "$((counter++)): $line"
    done
  • 相关阅读:
    20200304(10)
    20200303Tuesday(9)
    词根词缀explicit(8)
    词根词缀(7)
    20200303(6)
    什么是ring0-ring3
    20200301a
    mark字体大全
    评估评价 提高专项(5)
    图的广度优先遍历算法
  • 原文地址:https://www.cnblogs.com/dongling/p/5684296.html
Copyright © 2020-2023  润新知