• Shell编程之变量进阶


    一、变量知识进阶

    1.特殊的位置参数变量

    实例1:测试$n(n为1...15)

    [root@codis-178 ~]# cat p.sh
    echo $1
    [root@codis-178 ~]# sh p.sh oldboy
    oldboy
    [root@codis-178 ~]# sh p.sh oldboy oldgirl
    oldboy
    [root@codis-178 ~]# sh p.sh "oldboy oldgirl"
    oldboy oldgirl
    

    实例2:在脚本中同时加入$1和$2

    [root@codis-178 ~]# cat p.sh 
    echo $1 $2
    [root@codis-178 ~]# sh p.sh longge bingbing
    longge bingbing
    [root@codis-178 ~]# sh p.sh "longge bingbing" oldgirl
    longge bingbing oldgirl
    

    实例3:设置15个位置参数

    [root@codis-178 ~]# echo ${1..15}
    $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15
    [root@codis-178 ~]# cat n.sh 
    echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11 $12 $13 $14 $15
    [root@codis-178 ~]# echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    [root@codis-178 ~]# sh n.sh {a..z}
    a b c d e f g h i a0 a1 a2 a3 a4 a5
    # 位置参数大于9,输出内容就不对了
    
    # 数字大于9时,需要大括号将数字括起来
    [root@codis-178 ~]# cat n.sh 
    echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15}
    [root@codis-178 ~]# sh n.sh {a..z}
    a b c d e f g h i j k l m n o
    

    实例4:获取脚本的名称及路径

    [root@codis-178 ~]# cat n.sh 
    echo $0
    # 不带路径时为脚本名
    [root@codis-178 ~]# sh n.sh 
    n.sh
    # 全路径执行时为路径加脚本名
    [root@codis-178 ~]# sh /data/cron/n.sh 
    /data/cron/n.sh
    

    实例5:dirname和basename的用法

    [root@codis-178 ~]# dirname /data/cron/n.sh 
    /data/cron
    [root@codis-178 ~]# basename /data/cron/n.sh 
    n.sh
    

    实例6:利用$0和dirname、basename分别取脚本名称和路径

    [root@codis-178 ~]# cat /data/cron/n.sh 
    dirname $0
    basename $0
    [root@codis-178 ~]# sh /data/cron/n.sh 
    /data/cron
    n.sh
    

    实例7:生产环境中的脚本使用说明

    [root@codis-178 ~]# cat /data/cron/n.sh 
    echo $"Usage: $0 {start|stop|status|restart|reload}"
    [root@codis-178 ~]# sh /data/cron/n.sh 
    Usage: /data/cron/n.sh {start|stop|status|restart|reload}
    

    实例8:通过$#获取脚本传参的个数

    [root@codis-178 ~]# cat n.sh 
    echo $1 $2 $3 $4 $5 $6 $7 $8 $9
    echo $#
    [root@codis-178 ~]# sh n.sh {a..z}
    a b c d e f g h i
    26
    

    实例9:根据用户在命令行的传参个数判断用户的输入,不合要求的给予提示并退出

    [root@codis-178 ~]# cat t1.sh 
    [ $# -ne 2 ] && {
    	echo "must two args"
    	exit 1
    }
    echo oldgirl
    [root@codis-178 ~]# sh t1.sh
    must two args
    [root@codis-178 ~]# sh t1.sh arg1 arg2
    oldgirl
    
    # 改良版
    [root@codis-178 ~]# cat t2.sh 
    if [ $# -ne 2 ]
    	then
    		echo "USAGE:/bin/sh $0 arg1 arg2"
    
    		exit 1
    fi
    echo $1 $2
    [root@codis-178 ~]# sh t2.sh 
    USAGE:/bin/sh t2.sh arg1 arg2
    [root@codis-178 ~]# sh t2.sh oldboy oldgirl
    oldboy oldgirl
    

    实例10:利用set设置位置参数,测试$#和$@

    # 设置三个字符串参数,--表示清除所有参数变量,重新设置后面的参数变量
    [root@codis-178 ~]# set -- "I am" handsome oldboy
    [root@codis-178 ~]# echo $#
    3
    [root@codis-178 ~]# echo $1
    I am
    [root@codis-178 ~]# echo $2
    handsome
    [root@codis-178 ~]# echo $3
    oldboy
    
    # 不带双引号
    [root@codis-178 ~]# echo $*
    I am handsome oldboy
    [root@codis-178 ~]# echo $@
    I am handsome oldboy
    [root@codis-178 ~]# for i in $*;do echo $i;done
    I
    am
    handsome
    oldboy
    [root@codis-178 ~]# for i in $@;do echo $i;done
    I
    am
    handsome
    oldboy
    
    # 带双引号
    [root@codis-178 ~]# echo "$*"
    I am handsome oldboy
    [root@codis-178 ~]# echo "$@"
    I am handsome oldboy
    [root@codis-178 ~]# for i in "$*";do echo $i;done  # 当做一个参数
    I am handsome oldboy
    [root@codis-178 ~]# for i in "$@";do echo $i;done  # 每个参数独立输出
    I am
    handsome
    oldboy
    

    实例11:生产环境中的引用,分析以下语句含义runlevel=$(set -- $(runlevel); eval "echo $$#" )

    # set是设置参数,这里设置的是runlevel的返回值作为参数
    
    # runlevel命令拿到运行级别
    [root@codis-178 ~]# runlevel 
    N 3
    
    # $#是取命令有几个参数,上面的命令有2个返回参数
    
    # $$#,就是$2,因为$#=2
    
    # eval是对之后的命令进行二次扫描,把echo的字符串,当做命令解析,首先扫描到echo输出$2,第二次解析$2的值为等级3
    

    2.特殊的状态变量

    实例1:执行命令后获取返回值

    # 不同命令的执行结果中的返回值也不相同,0为成功,非0为失败
    [root@codis-178 ~]# pwd
    /root
    [root@codis-178 ~]# echo $?
    0
    [root@codis-178 ~]# ls /eee
    ls: cannot access /eee: No such file or directory
    [root@codis-178 ~]# echo $?
    2
    [root@codis-178 ~]# oldboy
    -bash: oldboy: command not found
    [root@codis-178 ~]# echo $?
    127
    

    实例2:根据返回值来判断软件的安装步骤是否成功

    [root@codis-178 fping-3.10]# ./configure prefix=/usr/local/fping
    ...略过部分过程
    configure: creating ./config.status
    config.status: creating Makefile
    config.status: creating doc/Makefile
    config.status: creating src/Makefile
    config.status: creating config.h
    config.status: executing depfiles commands
    [root@codis-178 fping-3.10]# echo $?
    0
    [root@codis-178 fping-3.10]# make
    ...略过部分过程
    make[2]: Leaving directory `/root/fping-3.10'
    make[1]: Leaving directory `/root/fping-3.10'
    [root@codis-178 fping-3.10]# echo $?
    0
    [root@codis-178 fping-3.10]# make install
    ...略过部分过程
    make[2]: Leaving directory `/root/fping-3.10'
    make[1]: Leaving directory `/root/fping-3.10'
    [root@codis-178 fping-3.10]# echo $?
    0
    

    实例3:通过获取$?的返回值确定网站备份是否正确

    [root@codis-178 ~]# tar czvf /root/fping-3.10.tar.gz ./fping-3.10
    ...略过部分过程
    ./fping-3.10/config.guess
    ./fping-3.10/config.h
    [root@codis-178 ~]# echo $?
    0
    

    实例4:通过脚本控制命令及脚本执行后的返回值

    [root@codis-178 ~]# cat test4.sh 
    [ $# -ne 2 ] && {
    	echo "must be two args."
    	exit 119 # 终止程序运行并指定119状态值赋值给$?
    }
    echo oldgirl
    [root@codis-178 ~]# sh test4.sh 
    must be two args.
    [root@codis-178 ~]# echo $?
    119
    [root@codis-178 ~]# sh test4.sh a1 a2
    oldgirl
    [root@codis-178 ~]# echo $?
    0
    

    在生产环境中的用法总结:

    • 判断命令、脚本、函数等程序是否执行成功
    • 若在脚本中调用执行"exit 数字",则会返回这个数字给"$?"变量
    • 如果是在函数里,则通过"return 数字"把这个数字以函数返回值的形式传给"$?"

    实例5:获取脚本执行的进程号

    [root@codis-178 ~]# cat test_pid.sh 
    echo $$ > /tmp/a.pid
    sleep 60
    [root@codis-178 ~]# sh test_pid.sh &
    [1] 1733
    [root@codis-178 ~]# ps -ef |grep test_pid|grep -v grep
    root      1733 28243  0 14:44 pts/0    00:00:00 sh test_pid.sh
    [root@codis-178 ~]# cat /tmp/a.pid 
    1733
    

    实例6:实现系统中多次执行某一个脚本后的进程只有一个

    [root@codis-178 ~]# cat pid.sh 
    #!/bin/sh
    
    pidpath=/tmp/a.pid
    if [ -f "$pidpath" ]
    	then
    		kill `cat $pidpath` > /dev/null 2>&1
    		rm -f $pidpath
    fi
    echo $$ > $pidpath
    sleep 60
    [root@codis-178 ~]# sh pid.sh &
    [1] 4142
    [root@codis-178 ~]# ps -ef |grep pid.sh|grep -v grep
    root      4142 28243  0 15:27 pts/0    00:00:00 sh pid.sh
    [root@codis-178 ~]# sh pid.sh &
    [2] 4158
    [root@codis-178 ~]# 
    [1]-  Terminated              sh pid.sh
    [root@codis-178 ~]# ps -ef |grep pid.sh|grep -v grep
    root      4158 28243  0 15:27 pts/0    00:00:00 sh pid.sh
    

    实例7:$_获取上一条命令的最后一个参数

    [root@codis-178 ~]# /etc/init.d/ntpd start oldboy
    Starting ntpd:                                             [  OK  ]
    [root@codis-178 ~]# echo $_
    oldboy
    [root@codis-178 ~]# /etc/init.d/ntpd stop oldgirl
    Shutting down ntpd:                                        [  OK  ]
    [root@codis-178 ~]# echo $_
    oldgirl
    

    实例8:$!获取上一次执行脚本的pid

    [root@codis-178 ~]# ps -ef |grep pid.sh|grep -v grep
    [root@codis-178 ~]# sh pid.sh &
    [1] 4439
    [root@codis-178 ~]# echo &!
    [2] 4450
    
    [root@codis-178 ~]# ps -ef |grep pid.sh|grep -v grep
    root      4439 28243  0 15:31 pts/0    00:00:00 sh pid.sh
    

    二、Shell内置变量命令

    1.常用的内部命令

    echo、eval、exec、export、read、shift

    2.内部命令用法

    (1)echo:在屏幕输出信息

    示例:

    [root@codis-178 ~]# echo oldboy;echo oldgirl
    oldboy
    oldgirl
    [root@codis-178 ~]# echo -n oldboy;echo oldgirl
    oldboyoldgirl
    [root@codis-178 ~]# echo "oldboy	oldgirl
    oldboy	oldgirl"
    oldboy	oldgirl
    oldboy	oldgirl
    [root@codis-178 ~]# echo -e "oldboy	oldgirl
    oldboy	oldgirl"
    oldboy	oldgirl
    oldboy	oldgirl    
    [root@codis-178 ~]# printf "oldboy	oldgirl
    oldboy	oldgirl"
    oldboy	oldgirl
    oldboy	oldgirl
    [root@codis-178 ~]# echo -e "123"
    23
    [root@codis-178 ~]# printf "123"
    23
    

    (2)eval:当Shell执行到eval语句时,读入参数args,并将它们组合成一个新的命令执行
    示例:

    [root@codis-178 ~]# cat noeval.sh 
    echo $$#
    [root@codis-178 ~]# sh noeval.sh arg1 arg2
    $2
     
    [root@codis-178 ~]# cat noeval.sh 
    eval "echo $$#"
    [root@codis-178 ~]# sh noeval.sh arg1 arg2
    arg2
    # 使得打印的特殊位置参数重新解析输出,而不是$2
    

    (3)exec:在不创建新的子进程的前提下,转去执行指定的命令,当命令执行完后,该进程终止
    示例:

    [root@codis-178 ~]# exec date
    Wed Jul 12 15:46:37 CST 2017
    [xiaoda@codis-178 ~]$
    # 退到普通用户下
    
    # exec打开文件后,read每次将文件指针移动到下一行进行读取,直到文件末尾
    [xiaoda@codis-178 ~]$ seq 5 > /tmp/tmp.log
    [xiaoda@codis-178 ~]$ cat exec.sh 
    exec < /tmp/tmp.log
    while read line
    do
    	echo $line
    done
    echo ok
    [xiaoda@codis-178 ~]$ sh exec.sh 
    1
    2
    3
    4
    5
    ok
    

    (4)read:从标准输入读取字符串等信息,传给Shell内部定义的变量

    (5)shift:每使用一次,会使所有位置参数依次向左移动一个位置,并使$#减1,直到0为止
    示例:

    [root@codis-178 ~]# cat n.sh 
    echo $1 $2
    if [ $# -eq 2 ];then
    	shift
    	echo $1
    fi
    [root@codis-178 ~]# sh n.sh 1111 2222
    1111 2222
    2222
    

    应用场景:
    当shell命令行的命令通过参数控制不同的功能时,例如sh shift.sh -c oldboy,我们就拿到了第二个参数

    (6)exit:退出Shell程序,可在后面指定一个数位作为返回值

    三、Shell变量子串

    1.子串介绍


    2.子串的实践

    # 定义一个变量
    [root@codis-178 ~]# OLDBOY="I am oldboy"
    [root@codis-178 ~]# echo ${OLDBOY}
    I am oldboy
    [root@codis-178 ~]# echo $OLDBOY
    I am oldboy
    
    # 返回变量值得长度
    [root@codis-178 ~]# echo ${#OLDBOY}
    11
    
    # 截取变量内容,也可理解为删除前面的多个字符
    [root@codis-178 ~]# echo ${OLDBOY:2}
    am oldboy
    [root@codis-178 ~]# echo ${OLDBOY:3}
    m oldboy
    [root@codis-178 ~]# echo ${OLDBOY:4}
    oldboy
    [root@codis-178 ~]# echo ${OLDBOY:2:2}
    am
    [root@codis-178 ~]# echo ${OLDBOY:2:5}
    am ol
    
    # 从变量内容的开头删除最短匹配"a*C"以及"a*c"的子串
    [root@codis-178 ~]# echo ${OLDBOY#a*C}
    123ABCabc
    [root@codis-178 ~]# echo ${OLDBOY#a*c}
    ABC123ABCabc
    
    # 从变量内容的开头删除最长匹配"a*C"以及"a*c"的子串
    [root@codis-178 ~]# echo ${OLDBOY##a*C}
    abc
    [root@codis-178 ~]# echo ${OLDBOY##a*c}
    
    # 从变量内容的结尾删除最短匹配"a*C"以及"a*c"的子串
    [root@codis-178 ~]# echo ${OLDBOY%a*C}
    abcABC123ABCabc
    [root@codis-178 ~]# echo ${OLDBOY%a*c}
    abcABC123ABC
    
    # 从变量内容的结尾删除最长匹配"a*C"以及"a*c"的子串
    [root@codis-178 ~]# echo ${OLDBOY%%a*C}
    abcABC123ABCabc
    [root@codis-178 ~]# echo ${OLDBOY%%a*c}
    
    # 用oldgirl代替变量中匹配oldboy的子串
    [root@codis-178 ~]# echo ${OLDBOY/oldboy/oldgirl}  # 替换第一个子串
    I am oldgirl,yes,oldboy
    [root@codis-178 ~]# echo ${OLDBOY//oldboy/oldgirl} # 全部替换
    I am oldgirl,yes,oldgirl
    

    3.子串在生产环境中的应用

    去掉下面所有文件的文件名中的_finished字符串

    [root@codis-178 log]# ls -l *.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_1_finished.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_2_finished.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_3_finished.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_4_finished.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_5_finished.log
    
    [root@codis-178 log]# for f in `ls *fin*.log`;do mv $f `echo ${f//_finished/}`;done
    
    [root@codis-178 log]# ls -l *.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_1.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_2.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_3.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_4.log
    -rw-r--r-- 1 root root 0 Jul 12 16:29 stu_102999_5.log
    
    进阶版1:
    ls |awk -F 'finished' '{print "mv "$0" "$1$2" "}'|/bin/bash
    
    进阶版2:
    rename "_finished" ""  *
    

    说明:
    1)${f//_finished/}利用变量子串替换功能把变量$f里的finished替换为空
    2)利用for循环依次拿到文件名
    3)利用mv命令修改,注意目标命令要用反引号括起来

    面试题:
    编写Shell脚本以打印下面语句中字符数小于6的单词
    I am oldboy linux,welcome to our training.

    四、特殊扩展变量

    1.特殊扩展变量介绍

    2.特殊扩展变量实践

    # ${parameter:-word}作用是如果parameter变量值为空或未赋值,则返回word字符串替代变量的值
    [root@codis-178 log]# echo $test
    
    [root@codis-178 log]# result=${test:-UNSET}
    [root@codis-178 log]# echo $result
    UNSET
    
    [root@codis-178 log]# test=oldboy
    [root@codis-178 log]# echo ${test}
    oldboy
    [root@codis-178 log]# result=${test:-UNSET}
    [root@codis-178 log]# echo $result   # 变量是否已定义
    oldboy
    [root@codis-178 log]# result=${test-UNSET}
    [root@codis-178 log]# echo $result
    oldboy
    
    # ${parameter:=word}作用是如果变量值为空或未赋值,就设置这个变量值为word,并返回其值
    (位置变量和特殊变量不适用)
    [root@codis-178 log]# unset result  # 撤销result变量定义
    [root@codis-178 log]# echo $result
    
    [root@codis-178 log]# unset test
    [root@codis-178 log]# echo $test
    
    [root@codis-178 log]# result=${test:=UNSET}
    [root@codis-178 log]# echo $result
    UNSET
    [root@codis-178 log]# echo $test
    UNSET
    
    # ${parameter:?word}作用是如果变量值为空或未赋值,word字符串将被作为标准错误输出
    # 用于变量未定义而报错的具体内容
    [root@codis-178 log]# echo ${key:?not defined}
    -bash: key: not defined
    [root@codis-178 log]# key=1
    [root@codis-178 log]# echo ${key:?not defined}
    1
    [root@codis-178 log]# unset key
    [root@codis-178 log]# echo ${key:?not defined}
    -bash: key: not defined
    
    # ${parameter:+word}作用是如果变量值为空或未赋值,则什么都不做,否则word字符串将替代变量的值
    [root@codis-178 log]# oldboy=${oldgirl:+word}
    [root@codis-178 log]# echo $oldboy
    
    [root@codis-178 log]# oldgirl=19
    [root@codis-178 log]# oldboy=${oldgirl:+word}
    [root@codis-178 log]# echo $oldboy
    word
    

    3.特殊扩展变量在生产环境中的应用案例

    针对目录路径等的处理可以采用上述变量不存在的方式,防止因目录路径不存在而导致的异常。

    实例:删除7天前的过期数据备份
    如果忘记了定义path变量,又不希望path值为空,就可以定义/tmp替代path为返回值

    [root@codis-178 log]# cat del.sh 
    find ${path-/tmp} -name "*.log" -type f -mtime +7|xargs rm -f
    [root@codis-178 log]# sh -x del.sh 
    + find /tmp -name '*.log' -type f -mtime +7
    + xargs rm -f
    
  • 相关阅读:
    Hibernate个人学习笔记(1)
    wangzhi
    星星评分
    关于jquery中jQuery slideToggle() 方法实现的原理
    css盒模型和块级、行内元素深入理解display:in
    html 界面跳转
    wxgz平台开发
    关于Apache PHP实现无后缀名 URL重写
    读书笔记
    比较好的书本
  • 原文地址:https://www.cnblogs.com/tongxiaoda/p/7454341.html
Copyright © 2020-2023  润新知