shell的几个重要命令,主要参数,循环语句以及变量处理

======================================================================
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 kadefor@gmail.com
    # 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]:
   

转载于:https://my.oschina.net/u/3251865/blog/839205

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值