======================================================================
 ule-scripts-shell
 ======================================================================
———————————————————————-
 shell02
 ———————————————————————-
:尚观科技: http://uplooking.com
.. contents::
 .. sectnum::
shell中重要的几个内部命令
 ———————————————————————-
* eval <args>       # 把后面的args当作一行命令来执行
 * exec <args>       # 后面跟命令作为参数, 它不会去创建新的子进程, 也就是说, 它结束后, 脚本就结束了, 不会再执行exec后面的语句了!!
|    [nich4@server4 ~]$ cat a.sh
 |    #!/bin/sh
 |    #
 |    echo aaaa
 |    exec \’ls\’
 |    echo bbbb
 |    [nich4@server4 ~]$ chmod +x a.sh
 |    [nich4@server4 ~]$ ./a.sh
 |    aaaa
 |    a.sh
 |    [nich4@server4 ~]$
 * readonly      # 定义一个只读变量, 当前shell中不可更改,不可unset
  
     可以先定义变量var, 再执行readonly var
也可以直接执行readonly var=value
* read # 可以一次要求输入多个变量, 由空格分隔
|    [root@localhost nich4]# read a b c d e
 |    1 2 3 4 5
 |    [root@localhost nich4]# echo $a $b $c $d $e
 |    1 2 3 4 5
 |    [root@localhost nich4]#
* wait      # 使Shell等待在后台启动的所有子进程结束。wait的返回值总是真
 * exit      # 退出Shell程序。在exit之后可有选择地指定一个数作为返回状态
 * shift     # 按下面的方式重新命名所有的位置参数变量,即$2成为$1,$3成为$2…在脚本中每使用一次shift语句,都使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减到0为止
 * .         # 点.  让shell读入指定的脚本文件, 并依次执行那个脚本中的语句, (即把脚本调入到当前shell中来执行)
 * :         # : 空命令 NOP no op 相当于true,返回0
 bash程序的调试
 ———————————————————————-
在编程过程中难免会出错,有的时候,调试程序比编写程序花费的时间还要多,Shell程序同样如此。
Shell程序的调试主要是利用bash命令解释程序的选择项。调用bash的形式是:
>> bash <参数> file
几个常用的选择项是:
-e 如果一个命令失败就立即退出。
-n 读入命令但是不执行它们。
-u 执行时把未设置的变量看做出错。
-v 当读入Shell命令时把它们显示出来。
-x 执行命令时把命令和它们的参数显示出来。
上面的所有选项也可以在Shell程序内部用\”set -<>\”的形式引用,而\”set +<>\”则将禁止该选择项起作用。如果只想对程序的某一部分使用某些选择项时,则可以将该部分用上面两个语句包围起来。
(1) 未置变量退出和立即退出
未置变量退出特性允许用户对所有变量进行检查,如果引用了一个未赋值的变量就终止Shell程序的执行。Shell通常允许未置变量的使用,在这种情况下,变量的值为空。如果设置了未置变量退出选择项,则一旦使用了未置变量就显示错误信息,并终止程序的运行。未置变量退出选择项为-u。
当Shell运行时,若遇到不存在或不可执行的命令、重定向失败或命令非正常结束等情况时,如果未经重新定向,该出错信息会显示在终端屏幕上,而Shell程序仍将继续执行。要想在错误发生时迫使Shell程序立即结束,可以使用-e选项将Shell程序的执行立即终止。
(2) Shell程序的跟踪
调试Shell程序的主要方法是利用Shell命令解释程序的-v或-x选项来跟踪程序的执行。-v选择项使Shell在执行程序的过程中,把它读入的每一个命令行都显示出来,而-x选择项使Shell在执行程序的过程中把它执行的每一个命令在行首用一个+加上命令名显示出来。并把每一个变量和该变量所取的值也显示出来。因此,它们的主要区别在于:在执行命令行之前无-v,则显示出命令行的原始内容,而有-v时则显示出经过替换后的命令行的内容。
除了使用Shell的-v和-x选择项以外,还可以在Shell程序内部采取一些辅助调试的措施。例如,可以在Shell程序的一些关键地方使用echo命令把必要的信息显示出来,它的作用相当于C语言中的printf语句,这样就可以知道程序运行到什么地方及程序目前的状态。
shell命令替换与扩展
 ———————————————————————-
* *命令替换* 很简单, 就两种情况: $( ) 和 \\` \\`
* *路径扩展:* 通配符 \\* ? [] {}
.. note::
花括号{}和中括号[]: 可以生成列表(list),其中\\*号以逗号分隔.[]不需要分隔
特殊的: {1..10} # 生成1到10的序列, 类似的还有\”seq 1 10\”
 shell参数
 ———————————————————————-
* 位置参数: 脚本运行时传给脚本的参数   $1, $2 ,… ${10}, ${100}
 * 内部参数:
|    $#    # 参数个数
 |    $*    # 所有参数的字符串
 |    $@    # 所有参数, 每个参数为一个字符串
 |    $?    # 上一条命令执行后的返回值
 |    $$    # PID
 |    $0    # 当前shell的名字
分支语句 流程控制
 ———————————————————————-
if语句
 “““““““““““““““““““““““““““““““““““
::
    if [ condition ];then
         command
     fi
     ———————
     if [ condition ];then
         command
     else
         command
     fi
     ———————
     if [ condition ];then
         command
     elif [ condition ];then
         command
     else
         command
     fi
     ———————-
     if [ condition ];thne
         command
         command
         if [ condition ];then
             command
         fi
     else
         if [ condition ];then
             command
         elif [ condition ];then
             command
         else
             command
         fi
     fi  
case语句
 “““““““““““““““““““““““““““““““““““
*标准用法:*
::
    case \”$arg\” in
         arg1 | arg01)   # | 或者
             cmd
             ;;
         arg2)
             cmd
             ;;
         *)
             cmd
             ;;
     esac
         
 **eg:**
::
    case $1 in  
             start | begin)  
               echo \”start\”  
             ;;  
             stop | end)  
               echo \”stop\”  
             ;;  
             \\*)  
               echo \”Ignorant\”  
             ;;  
     esac
**eg:** 判断系统
     
 有时候,我们需要写跨平台的脚本, 那么我们就需要判断一下脚本运行在什么平台, 然后再使用相应的方法来实现,如Linux、FreeBSD、Solaris等等::
    #!/bin/sh  
     SYSTEM=`uname -s`  
     
     case \”$SYSTEM\” in  
         Linux)  
             echo \”My system is Linux\”  
             echo \”Do Linux stuff here…\”  
         ;;  
         FreeBSD)  
             echo \”My system is FreeBSD\”  
             echo \”Do FreeBSD stuff here…\”  
         ;;  
         *)  
             echo \”Unknown system : $SYSTEM\”  
             echo \”I don\’t what to do…\”  
         ;;  
     esac
.. note::
case语句的匹配是从上往下的顺序, 因此我们编写的原则也是从上往下
case语句的模板支持匹配: * ? []
    * 匹配以n开头的所有情况: n*
     * 匹配yes的所有字母大小不同的情况: [yY][eE][sS]
     * 但不支持{}匹配,因为可以使用 | 就可以达到目的。
循环语句
 ———————————————————————-
for循环
 “““““““““““““““““““““““““““““““““““
标准用法:
::
     
     for xx  in xx xxx xx
     do
         command
     done
     for (( i=0 ; i<5 ; i++ ))     #   定义一个变量,i<5 循环的退出条件,i++ 代表自加1
     do
         command;
         command;
     done
    for (( i=30 ; i>0 ; i– ))     #   定义一个变量,i<5 循环的退出条件,i++ 代表自加1
     do
         command;
         command;
     done
 while循环
 “““““““““““““““““““““““““““““““““““
实现如果不确定循环的次数,这时候就要用到while
标准用法:
::
    while [ condition ]  # 只要条件满足就继续循环
     do
         command
     done
until循环
 “““““““““““““““““““““““““““““““““““
当满足条件的时候,退出循环, 而while是不满足条件的时候才退出循环.
::
     
     until [ condition ] ; do
         command;
         command;
         …
         command;
     done
eg:
    $ VAR=10
     $ until [ \”$VAR\” -eq 0 ]; do
     >   echo $VAR;
     >   VAR=$(($VAR-1));
     > done
eg:
    rannum=`expr $RANDOM % 101`
     small=0
     big=100
read -p \”Input you guest:\” guest
    until [ \”$rannum\” -eq \”$guest\” ]  # $rannum = $guest 的时候就退出循环
     do
         [ … ] && command
done
select
 “““““““““““““““““““““““““““““““““““
select 多用来快速的设计一个字符界面的用户交互选择的菜单, 也多与case一起使用.
语法和 for 循环非常相似:
::
    select Key in List
     do
         Command;
     done
eg:
    #!/bin/sh
     #
    echo \”请选择:\”
     list=\”Success Fail Exit\”
    select a in $list;do
         if [ \”$a\” = \”Success\” ];then
             echo Success
         elif [ \”$a\” = \”Fail\” ];then
             echo Fail
         elif [ \”$a\” = \”Exit\” ];then
             exit
         else
             echo \”No the Choose\”
         fi
     done
 \”奇技淫巧\”
 “““““““““““““““““““““““““““““““““““
::
     
     shell_for_loop总结
     # Modified: 20101012-11:57:31 by [email protected]
     # Filename: loop-example.txt
     
     #!/bin/env bash
     #
     for i in 1 2 3 4 5
     do
         echo \”Welcome $i times\”
     done
     
     
     #/bin/env bash
     # bash version 3.0+
     #
     for i in {1..5}
     for i in $(seq 1 5)
     do
         echo \”Welcome $i times\”
     done
     
     #!/bin/env bash
     # bash version 4.0+
     # {START..END..INCREMENT}
     #
     echo \”Bash version ${BASH_VERSION}…\”
     for i in {0..10..2}
     do
         echo \”Welcome $i times\”
     done
     
     #!/bin/env bash
     #
     for i in `seq 1 2 20`
     do
         echo \”Welcome $i times\”
     done
     
     #!/bin/env bash
     # for (( EXP1; EXP2; EXP3 ))
     #
     for (( c=1; c<=5; c++ ))
     do
         echo \”Welcome $c times…\”
     done
     
     #!/bin/env bash
     # infinite loops
     #
     for (( ; ; ))    # other: 1. while true 2. while :
     do
         echo \”innfinite loops [ hit CTRL+C to stop]\”
     done
     
     #!/bin/env bash
     #
     #for I in 1 2 3 4 5
     #do
     #  statements1      #Executed for all values of \’\’I\’\’, up to a disaster-condition if any.
     #  statements2
     #  if (disaster-condition)
     #  then
     #    break              #Abandon the loop.
     #  fi
     #  statements3          #While good and, no disaster-condition.
     #done
     #
     for file in /etc/*
     do
         if [ \”${file}\” == \”/etc/resolv.conf\” ]
         then
             countNameservers=`grep -c nameserver /etc/resolv.conf`
             echo \”Total ${countNameservers} nameservers defined in ${file}\”
             break
         fi
     done
     
     
     #!/bin/env bash
     #
     #for I in 1 2 3 4 5
     #do
     #  statements1      #Executed for all values of \’\’I\’\’, up to a disaster-condition if any.
     #  statements2
     #  if (condition)
     #  then
     #    continue   #Go to next iteration of I in the loop and skip statements3
     #  fi
     #  statements3
     #done
     #
     FILES=\”$@\”
     for f in $FILES     #打印脚本的参数,此处可省略\”in $FILES\”,只需要\”for f\”
     do
         # if .bak backup file exists, read next file
         if [ -f ${f}.bak ]
         then
             echo \”Skiping $f file…\”
             continue
         fi
        # we are hear means no backup file exists, just use cp command to copy file
         /bin/cp $f $f.bak
     done
     
     #!/bin/env bash
     #
     FILE=/etc/passwd
     FS=\’:\’
     while read line
     do
         # store field 1
         F1=$(echo $line|cut -d$FS -f1)
         # store field 2
         F2=$(echo $line|cut -d$FS -f6)
         # store field
         F3=$(echo $line|cut -d$FS -f7)
         echo \”User \\\”$F1\\\” home directory is $F2 and login shell is $F3\”
     done < $FILE
 .. code:: bash
     
     例:
编写脚本,实现一个闹钟
    #!/bin/bash
     # 定时闹钟
     
     clock=$1
     
     while true
     do
     now=$(date +%H:%M:%S)
             if [ \”$now\” = \”$clock\”  ]
             then
                     echo -e \”$now time\’s up !\”
                     for (( i=0; i<5;i++ ))
                     do
                             echo -e -n \”\\a\”
                             sleep 1
                     done
                     break
             fi
     done
shell变量扩展
 ———————————————————————-
1. 变量定义与删除
| var_name=value   # 等号前后没有空格
 |                  # 字母和数字, 字母开头
 | kernel=$(uname -r)
 | kernel=`uname -r`
 | read var_name
 |
 | unset var_name
#. 变量引用与显示
 | $var_name
 | ${var_name}str  # 显示指定, 字符串合并
 | echo $var_name
#. 变量处理
需要用{}括起来.
* ${var:-default} # 如果变量var没有定义, 则临时使用\”default\”值
|    作用: 如果变量未定义,返回一个默认值
 |    
 |    [nich4@server4 ~]# echo ${var}
 |    
 |    [nich4@server4 ~]# echo ${var:-default}
 |    default
 |    [nich4@server4 ~]# echo ${var}
 |    
 |    [nich4@server4 ~]# var=new
 |    [nich4@server4 ~]# echo ${var:-default}
 |    new
 |    [nich4@server4 ~]#
 |    
* ${var:=default} # 如果变量var没有定义, 则把\”default\”值赋给变量var
|    作用: 如果变量未定义,为变量设初始值,并返回
 |    
 |    [nich4@server4 ~]$ echo $var
 |    
 |    [nich4@server4 ~]$ echo ${var:=default}
 |    default
 |    [nich4@server4 ~]$ echo $var
 |    default
 |    [nich4@server4 ~]$
.. note::
     
     变量替换的值也可以使用`(反引号):
echo ${var:-`pwd`}
* ${var:+default} # 如果var已经定义, 且为非空时, 则临时使用\”default\”值
|    作用: 测试一个变量是否存在,如果存在,返回default,不存在返回null
 |    
 |    [nich4@server4 ~]$ unset var
 |    [nich4@server4 ~]$ echo ${var:+default}
 |    
 |    [nich4@server4 ~]$ echo $var
 |    
 |    [nich4@server4 ~]$ var=old
 |    [nich4@server4 ~]$ echo ${var:+default}
 |    default
 |    [nich4@server4 ~]$ echo $var
 |    old
 |    [nich4@server4 ~]$ var=\’\’
 |    [nich4@server4 ~]$ echo ${var:+default}
 |    
 |    [nich4@server4 ~]$ echo $var
 |    
 |    [nich4@server4 ~]$
  
 * ${var:?message}       # 判断变量var是否定义,且是否非空
  
 |    作用: 捕获未定义变量导致的错误,即如果变量未定义的话,就输出一个错误信息
 |    
 |    [nich4@server4 ~]$ var=old
 |    [nich4@server4 ~]$ echo ${var:?\’Errors!\’}
 |    old
 |    [nich4@server4 ~]$ var=\’\’
 |    [nich4@server4 ~]$ echo ${var:?\’Errors!\’}
 |    -bash: var: Errors!
 |    [nich4@server4 ~]$ unset var
 |    [nich4@server4 ~]$ echo ${var:?\’Errors!\’}
 |    -bash: var: Errors!
 |    [nich4@server4 ~]$
 |    
 |    当message没有指定时,shell将显示一条默认的消息:
 |    [nich4@server4 ~]$ unset var
 |    [nich4@server4 ~]$ echo ${var:?}
 |    -bash: var: parameter null or not set
 |    [nich4@server4 ~]$ var=\’\’
 |    [nich4@server4 ~]$ echo ${var:?}
 |    -bash: var: parameter null or not set
 |    [nich4@server4 ~]$
  
 * ${var:begin:length}       # 截取字符串, 从begin开始截取length长度,不指定length的话就是从begin开始到结尾
  
 |    作用: 返回变量值的子串
 |    
 |    0开始记数, 即begin最小为0, 然后从begin开始截取length个字符
 |    
 |    [nich4@server4 ~]$ var=\’uplooking.com\’
 |    [nich4@server4 ~]$ echo ${var:2:7}
 |    looking
 |    [root@localhost nich4]#  echo ${var:2}
 |    looking.com
 |    [nich4@server4 ~]$
  
 * ${#var}       # 变量var的值的字符数
  
 |    [nich4@server4 ~]$ echo $var
 |    uplooking.com
 |    [nich4@server4 ~]$ echo ${#var}
 |    13
 |    [nich4@server4 ~]$
  
 * ${var#pattern}    # 去掉变量值开头的与pattern相匹配的部分, 最短匹配模式
 * ${var##pattern}   # 同上, 但为最长匹配模式
  
 * ${var%pattern}    # 去掉变量值结尾的与pattern相匹配的部分, 最短匹配模式
 * ${var%%pattern}   # 同上, 但为最长匹配模式
  
 * ${var/pattern/str}    # 用str替换var中的第一个pattern
 * ${var//pattern/str}   # 替换var中所有的pattern
  
 |    [nich4@server4 ~]$ var=\’/etc/sysconfig/network-scripts/ifcfg-eth0\’
 |    [nich4@server4 ~]$ echo ${var#*sc}
 |    onfig/network-scripts/ifcfg-eth0
 |    [nich4@server4 ~]$ echo ${var##*sc}
 |    ripts/ifcfg-eth0
 |    
 |    [nich4@server4 ~]$ echo $var
 |    /etc/sysconfig/network-scripts/ifcfg-eth0
 |    [nich4@server4 ~]$ echo ${var%sc*}
 |    /etc/sysconfig/network-
 |    [nich4@server4 ~]$ echo ${var%%sc*}
 |    /etc/sy
 |    
 |    [nich4@server4 ~]$ echo $var
 |    /etc/sysconfig/network-scripts/ifcfg-eth0
 |    [nich4@server4 ~]$ echo ${var/sc/XX}
 |    /etc/syXXonfig/network-scripts/ifcfg-eth0
 |    [nich4@server4 ~]$ echo ${var//sc/XX}
 |    /etc/syXXonfig/network-XXripts/ifcfg-eth0
 |    [nich4@server4 ~]$ echo ${var//sc/}     # 删除
 |    /etc/syonfig/network-ripts/ifcfg-eth0
 |    [nich4@server4 ~]$
 |    
 |    如果模式以#开头,则必须匹配variable的开头
 |    如果模式以%开头,则必须匹配var的结尾
 |    如果string为null,匹配部分将被删除
 |    如果var为@或*,操作被依次应用于每个位置参数并且扩展为结果列表
算术运算
 ———————————————————————-
* $(( ))
|    [root@localhost nich4]# echo $((4+5))
 |    9
 |    [root@localhost nich4]# echo $((4-5))
 |    -1
 |    [root@localhost nich4]# echo $((4*5))
 |    20
 |    [root@localhost nich4]# echo $((4/5))
 |    0
 |    [root@localhost nich4]# echo $((4%5))     # 求余
 |    4
 |    [root@localhost nich4]# echo $((4**5))    # 乘幂
 |    1024
 |    [root@localhost nich4]#
* $[ ]
|    [root@localhost nich4]# echo $[4+5]
 |    9
 |    [root@localhost nich4]# echo $[4-5]
 |    -1
 |    [root@localhost nich4]# echo $[4*5]
 |    20
 |    [root@localhost nich4]# echo $[4/5]
 |    0
 |    [root@localhost nich4]# echo $[4%5]
 |    4
 |    [root@localhost nich4]# echo $[4**5]
 |    1024
 |    [root@localhost nich4]#
* expr # 外部命令
|    [root@localhost nich4]# expr 4 + 5
 |    9
 |    [root@localhost nich4]# expr 4 – 5
 |    -1
 |    [root@localhost nich4]# expr 4 / 5
 |    0
 |    [root@localhost nich4]# expr 4 \\* 5
 |    20
 |    [root@localhost nich4]# expr 4 % 5        
 |    4
 |    [root@localhost nich4]# expr 4 \\*\\* 5     # 没有乘幂
 |    expr: syntax error
 |    [root@localhost nich4]# expr 4 ** 5
 |    expr: syntax error
 |    [root@localhost nich4]#
.. note::
运算符两边要有空格! \\*号需要转义!
* let
|    [root@localhost nich4]# let var=4+5
 |    [root@localhost nich4]# echo $var
 |    9
 |    [root@localhost nich4]# let var=4*5
 |    [root@localhost nich4]# echo $var
 |    20
 |    [root@localhost nich4]# let var=4%5
 |    [root@localhost nich4]# echo $var
 |    4
 |    [root@localhost nich4]# let var=4-5
 |    [root@localhost nich4]# echo $var
 |    -1
 |    [root@localhost nich4]# let var=4**5
 |    [root@localhost nich4]# echo $var
 |    1024
* bc
|    [root@localhost nich4]# echo \”4+5\” | bc
 |    9
 |    [root@localhost nich4]# echo \”4*5\” | bc
 |    20
 |    [root@localhost nich4]# echo \”4-5\” | bc
 |    -1
 |    [root@localhost nich4]# echo \”4/5\” | bc
 |    0
 |    [root@localhost nich4]# echo \”4**5\” | bc
 |    (standard_in) 1: parse error
 |    [root@localhost nich4]# echo \”4^5\” | bc
 |    1024
 |    [root@localhost nich4]#
.. note::
    bc是一个功能强大的计算器! 另外, bc只接受标准输入, 非交互下, 除了使用管道, 我们还可以使用\”here string\”(<<<):
     bc <<< 4^5
.. note::
在脚本中我们经常需要递增(加), 下面的方法都是可以的(参考):
    i=$[i+1]
     $[i++]
     $[++i]
i=`expr $i + 1`
i=$(($i+1))
    $((i++))
     ((i++))
let i=i+1
let i++
let $[i++]
i=`bc<<<$i+1`
for ((i=0;i<5;i++)) …
 练习作业:
 ———————————————————————-
1. 把用户指定目录下的所有空文件删除,最后输出删除的文件个数
     delempty.sh
 #. 使用for循环批量添加19个用户,用户名分别是user01~user19,密码为uplooking。添加后批量删除
     addusers.sh   delusers.sh
    01 02 03 …
     1 2 3 …
 #. 创建一个/tmp/test目录,里面有3个文件。名字分别为111,222,333。写一个脚本,可以每隔3秒钟循环对这3个文件改名。把111改为222,222改为333,333改为111
     rename.sh
 #. 使用for循环测试局域网内(192.168.50.0/24)服务器的网络状态(UP|DOWN)
     ping_serv.sh
#. 提取出/usr/share/doc目录下的所有的文件名为index.html的文件。把他们集中放在/tmp/index目录中。文件名字按提取顺序更名标记。即:第一个找到的index.html命名为index.html.1。第二个为index.html.2。以此类推
     find_index.sh
     
 #. 结合所学知识实现下面这个程序:
实现功能菜单:
执行脚本后
按1,显示当前时间
按2,显示剩余内存
按3,显示在线用户
按4,显示CPU负载
按5,退出脚本
按其他字符,提示超出选择范围后退出
::
     
     ————– Menu —————-
     
             1) Show current Time
             2) Memory Free
             3) Online Users
             4) CPU Load
             5) Exit
     
     ————————————
     Enter your choose [1-5]:
    
转载于:Shell/3251865
爱站程序员基地


