zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Java异常处理和设计
    一次qps测试实践
    Alternate Task UVA
    Just Another Problem UVA
    Lattice Point or Not UVA
    Play with Floor and Ceil UVA
    Exploring Pyramids UVALive
    Cheerleaders UVA
    Triangle Counting UVA
    Square Numbers UVA
  • 原文地址:https://www.cnblogs.com/dongling/p/5684296.html
Copyright © 2011-2022 走看看