zoukankan      html  css  js  c++  java
  • 六 BASH 高级变量

    高级变量分为三类

    变量扩展  ${变量名}                  例   ${filename}   大括号

    命令替换  $(命令)        $(ls /)    小括号

    算术扩展  $((算数式))      $((9+9))    小括号

     (一) 变量扩展:测试存在性及空值

    测试变量   是否存在的   基本用法

    ${待测变量-默认值}   如果该变量不存在,则赋值默认值。如果该变量存在,则显示变量值

    [root@localhost ShellScript]# echo $From          #利用上一章中的变量来测试
    From: me@example.edu.cn
    [root@localhost ShellScript]# r=${From-noAddress}
    [root@localhost ShellScript]# echo $r            #变量存在,则将变量值赋值给r
    From: me@example.edu.cn
    [root@localhost ShellScript]# unset From          #清楚该变量
    [root@localhost ShellScript]# echo $r
    From: me@example.edu.cn
    [root@localhost ShellScript]# r=${From-noAddress}    #重新判断赋值
    [root@localhost ShellScript]# echo $r                #打印默认值 noAddress
    noAddress

    这里有个注意的点。就是   变量存在与变量值为空的问题。${变量名-默认值}只会在变量不存在的时候,显示默认值,而如果存在该变量,但变量值为空的话,则会显示空,而非默认值。

    如果要将变量存在于变量值为空同时判断的话,则需要在-前面加一个:,才能在变量为空或变量值为空的时候显示默认值

    ${变量名:-默认值}

    [root@localhost ShellScript]# From=                #将该变量值设为空
    [root@localhost ShellScript]# r=${From-noAddress}        #赋值
    [root@localhost ShellScript]# echo $r               #可以发现,采用-赋值,他不会将默认值赋值给r,而会将空赋给r,打印如下   
    
    [root@localhost ShellScript]# r=${From:-noAddress}        #若想让变量显示默认值,则需要在-之前加上:,才能显示默认值,打印如下
    [root@localhost ShellScript]# echo $r             
    noAddress

    这一点,在写脚本赋值默认参数的时候,很实用。因为,很多时候,需要给脚本赋值,但是如果客户不赋值的话,一般会给一个默认值。就可以采用这种方式来操作

    例:

    [root@localhost ShellScript]# vim testmsg.sh
    #! /bin/bash
    filename=$1
    MSG=${filename:-"testmsg.txt"}
    cat > $MSG  <<here
    From: im@example.edu.cn
    To  : yr@example.edu.cn
    Subject:测试一下
    
    
    这是一封信,请勿回复
    here
     
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    "testmsg.sh" [新] 12L, 184C 已写入                            
    [root@localhost ShellScript]# sh testmsg.sh                #不赋值的时候,显示的是默认值testmsg.txt   ls打印一下,看看是否存在该文件,cat一下,看看文件内容是否正确
    [root@localhost ShellScript]# ls
    20090310.txt  create_prg.sh  hello  hello.c  testmsg.sh  testmsg.txt
    [root@localhost ShellScript]# cat testmsg.txt
    From: im@example.edu.cn
    To  : yr@example.edu.cn
    Subject:测试一下
    
    
    这是一封信,请勿回复
    [root@localhost ShellScript]# sh testmsg.sh testmsg2.txt  #赋值的时候,显示的是赋值之后的testmsg2.txt,ls并cat一下查验,证明脚本没有任何问题
    [root@localhost ShellScript]# ls
    20090310.txt   hello    testmsg2.txt  testmsg.txt
    create_prg.sh  hello.c  testmsg.sh
    [root@localhost ShellScript]# cat testmsg2.txt
    From: im@example.edu.cn
    To  : yr@example.edu.cn
    Subject:测试一下
    
    
    这是一封信,请勿回复

    特殊运用法

    [ -n ${DEBUG:-} ] && set -v

    该条语句一般用在script的开头,用来排错

    注:

    [ ] 是测试条件的语法  -n  来测试后接的变量是否有值     即  测不空   若非空,则传回真值

    &&之前的半句的意思就很清楚了。如果DEBUG为空,则默认值为空,则 -n 整个都为空值

    如果DEBUG不为空,则[-n] 不为空,则会执行后一句  set -v 来查错

    OK.这个命令,我操作之后,无论如何都会执行后面的  set -v  很纳闷,这个地方留意下,以后再补充,有可能我写的不对

    [root@localhost ShellScript]# vim testmsg.sh
    DEBUG="debug"
    echo $DEBUG
    echo 
    
    #[ -n ${DEBUG:-} ] && set -v              这里无论DEBUG是否为空,都会执行,很纳闷
    [ -n ${DEBUG:- } ] && echo hehe 
    echo $hehe
    #! /bin/bash
    filename=$2
    MSG=${filename:-"testmsg.txt"}
    cat > $MSG  <<here
    From: im@example.edu.cn
    To  : yr@example.edu.cn
    Subject:测试一下
    
    
    这是一封信,请勿回复
    here
    
    ~                                                                               
    ~                                                                               
    ~          

     OK.昨天测试的上述脚本,后面的set -v总是会进行打印,弄的我束手无策,去百度搜了一下,也没有什么进展,问了一下群里的朋友,群里的朋友说不能这么写,有点不信邪吧。。。呵呵,因为逻辑没错误的东西我一般都不大信邪

    今早来,又试了一下,这次我将${DEBUG:-}赋值给了一个变量,用[ -n ]来判断看看结果。结果依然。。。。。我在想,难道真如那位朋友所说,[ -n ]只能用于If 而不能用 && 链接?太扯淡了吧。。。继续百度搜了一下资料,突然在一篇文章中,我发现了一个新奇的东东

    如下图

    发现跟我代码的区别了么?他在-n 后面的变量,使用双引号引起来的,而我却没有用双引号,而  $[DEBUG:-]其实本身也是一种高级变量的形式。那是否我也应该用双引号来引起来呢?果然问题出在这里

    #DEBUG="debug"
    #echo $DEBUG
    #echo 
    #isDebug=${DEBUG:-}
    [ -n "${DEBUG:-}" ] && set -v   #双引号将变量括起来,才能让 -n 起作用
    #[ -n "$isDebug" ] && echo hehe 
    #echo $hehe
    #! /bin/bash
    filename=$2
    MSG=${filename:-"testmsg.txt"}
    cat > $MSG  <<here
    From: im@example.edu.cn
    To  : yr@example.edu.cn
    Subject:测试一下
    
    
    这是一封信,请勿回复
    here

     测试变量 “不存在”  或其值为空:则给空变量赋默认值

    [root@localhost ShellScript]# unset count
    [root@localhost ShellScript]# r=${count:=100} 
    [root@localhost ShellScript]# echo $count
    100
    [root@localhost ShellScript]# echo $r
    100

    从上例可以看出,r 和 count的值均赋值为了100

    这个例子的用法在于,给变量一个初始值。也就是给count一个初始值来做操作

    例如上上个例子的filename=$1中,是通过   MSG=${filename:-"testmsg.txt"}来给MSG赋值并操作的。那filename的值还是为空的。如果要将filename的默认值也设置为testmsg.txt则可以用MSG=${filename:="testmsg.txt"} 来操作执行

    测试变量是否 "不存在" 或其值为空:提示错误信息,并终止脚本程序运行

    语法为  ${待测变量:?提示信息}

    类似于 if [ 变量不存在 或 空值 ]; then

      显示变量名称: '提示信息'

      else

        继续执行下面的命令

      fi

    注意,就如上面的拆解所展示的一样,如果该变量为空,只会显示错误信息,而停止执行整个脚本程序。

    这个我觉得也很好用

    [root@localhost ShellScript]# vim fn.sh
    #! /bin/bash
    filename=${1:?“请输入要删除的路径”}
    echo "你要删除的目录指令为:"
    echo "rm -f $filename"
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                               
    ~                                                                                                                                                   
    "fn.sh" 4L, 124C 已写入                                       
    [root@localhost ShellScript]# sh fn.sh 
    fn.sh:行2: 1: “请输入要删除的路径”
    [root@localhost ShellScript]# sh fn.sh  testmsg.txt
    你要删除的目录指令为:
    rm -f testmsg.txt

    这样就能避免误删除操作

    测试变量的存在性

    语法  ${待测变量:+真值}

    判断条件:若变量存在且值非空,符合判断

    IamHappy="Feel so good"
    r=${IamHappy:+'true'}  #IamHappy该变量存在且非空,所以r变量的值应为true
    echo $r

    用途:用来测试某件事的真伪

    [root@localhost ShellScript]# vim fgrep.sh
    #! /bin/bash
    
    exec grep -F ${1:+"$@"}
    ~                                                                               
    ~                                                                                                                                                  
    "fgrep.sh" [新] 3L, 38C 已写入                                
    [root@localhost ShellScript]# sh fgrep.sh Mem /proc/meminfo /root/test.txt
    /proc/meminfo:MemTotal:        1863252 kB
    /proc/meminfo:MemFree:         1026988 kB
    /proc/meminfo:MemAvailable:    1236900 kB
    grep: /root/test.txt: 没有那个文件或目录

    上例中有两个点,一个是${:+}的语法,记得后面的变量也好,值也好都要加引号。

    在一个就是$@,估计大家会有点懵。。。没关系,找一下我以前的文章,在鸟哥那本书的介绍中,有这个介绍。总结一下

    $0: 脚本本身文件名称
    $1: 命令行第一个参数,$2为第二个,以此类推
    $*: 所有参数列表
    $@: 所有参数列表
    $#: 参数个数
    $$: 脚本运行时的PID
    $?: 脚本退出码
    
    ∗与@的区别
    
    当命令行为test.sh 1 2 3
    "$*“表示"1 2 3”
    "$@“表示"1” “2” “3”
    二者没有被引号括起来时是一样的都为"1 2 3",只有当被引号括起来后才表现出差异


    总结以上高级用法的应用。我就不写了,贴个图以示留念

     (二)变量扩展:取字符串切片,字符串长度。也就是所谓的截取字符串

    1  从某一位置开始到末尾截取

    语法一   ${变量:位置起点}有指定的位置开始,截取字符串到末尾

    [root@localhost ShellScript]# myname="Shell man"
    [root@localhost ShellScript]# substr=${myname:4}
    [root@localhost ShellScript]# echo $substr
    l man

    语法二:${变量:位置起点:长度}

    例子中继续沿用上面的myname

    [root@localhost ShellScript]# substr=${myname:4:4}
    
    [root@localhost ShellScript]# echo $substr 
    l ma
    #检验IFS分隔符参数
    [root@localhost ShellScript]# echo "_${IFS:1:1}_" #第二个参数tab
    _       _
    [root@localhost ShellScript]# echo "_${IFS:2:1}_" #第三个参数换行
    _
    _
    [root@localhost ShellScript]# echo "_${IFS:0:1}_"  #第一个参数空格
    _ _

    取部分位置参数

    这里用参数变量来举例子,即命令行参数

    ${@:起点}

    有起点开始,取得后面所有未知的参数

    解析:

    命令行参数第一个用$1,第二个用$2表示,以此类推。$@表示所有参数,在上节中有专门做过介绍。$0则表示命令本身。像$0,$1,$2,$3则成为位置参数

    ${@:起点:个数}

    从起点开始,取得个数个位置参数的值

    例:

    [root@localhost ShellScript]# vim poz.sh
    #! /bin/bash
    echo ${@:1}            #从第一个参数起,取出所有剩余参数
    echo ${@:1:2}         #从第一个参数起,取出两个参数    
    
    ~                                                                          
    "poz.sh" [新] 4L, 40C 已写入                                  
    [root@localhost ShellScript]# sh poz.sh 1 2 3
    1 2 3
    1 2    

    计算字符串长度

    语法 ${#变量}

    [root@localhost ShellScript]# filename="/usr/sbin/ntpdate"
    [root@localhost ShellScript]# echo ${#filename}
    17

    总结:

     其他方法:

    借助外部程序expr来计算

    通过expr的length选项来计算

    str="Here you are"
    len=$(expr length "$str")
    echo $len
    lenT=$(expr "$str" : '.*')
    echo $lenT

     expr “字符串” : '.*'

    :后接的.*是一个代表任意多个字符的字符串样式,expr会根据此样式来对比"字符串",等于是计算字符串长度。

    str='Here you are'
    len=$(expr "$str" : '.*')
    echo "str字符串长度为:$len"

    三   变量扩展: 对比样式

    解析:所谓对比样式,目的是截取变量值(字符串)的某一部分,做法是:将符合样式的部分字符串删除或取代。

     1  有字符串前面对比,删除相符者

    语法 :${变量#样式}

    解析:由前面(最左边)开始,对比变量值,删除“最短相符合的字符串”

    [root@localhost ~]# filename="/usr/sbin/ntpdate"
    [root@localhost ~]# r=${filename#/*/}
    [root@localhost ~]# echo $r
    sbin/ntpdate
    [root@localhost ~]# r=${filename#*/} 
    [root@localhost ~]# echo $r
    usr/sbin/ntpdate
    [root@localhost ~]# r=${filename#/*}#这个地方我不太理解,因为/*/这个我理解,表示两个斜杠之间有内容或为空字符串都会被删掉。那我/*是会删除/usr还是/后面的所有部分呢?结果是只把/删除了。。。。
    [root@localhost ~]# echo $r
    usr/sbin/ntpdate
    [root@localhost ~]# r=
    [root@localhost ~]# echo $r
    
    [root@localhost ~]# r=${filename#/*}
    [root@localhost ~]# echo $r         
    usr/sbin/ntpdate
    [root@localhost ~]# r=${filename#/usr}
    [root@localhost ~]# echo $r
    /sbin/ntpdate
    [root@localhost ~]# r=${filename#/usr/sbin/}
    [root@localhost ~]# echo $r
    ntpdate

    由前面比对,删除最长的

    语法:${变量##样式}

    解析:由前面最左边开始,对比变量值,删除"最长相符合的字符串"

    好麻烦,还是给个例子把这一块全部归拢一下吧。一下摘录  摘抄地址:https://www.cnblogs.com/wqbin/p/11597700.html

    假设我们定义了一个变量为:
    
    file=/dir1/dir2/dir3/my.file.txt
    
    我们可以用${ }分别替换获得不同的值:
    
    ${file#*/}:拿掉第一条/及其左边的字串:dir1/dir2/dir3/my.file.txt
    
    ${file##*/}:拿掉最后一条/及其左边的字串:my.file.txt
    
    ${file#*.}:拿掉第一个.及其左边的字串:file.txt
    
    ${file##*.}:拿掉最后一个.及其左边的字串:txt
    
    ${file%/*}:拿掉最后条/及其右边的字串:/dir1/dir2/dir3
    
    ${file%%/*}:拿掉第一条/及其右边的字串:(空值)
    
    ${file%.*}:拿掉最后一个.及其右边的字串:/dir1/dir2/dir3/my.file
    
    ${file%%.*}:拿掉第一个.及其右边的字串:/dir1/dir2/dir3/my
    复制代码

    注解:

    #是去掉左边 (在键盘上#在${}之左边)

    %是去掉右边(在键盘上%在${}之右边)

    #   % %:从左边数第一条,从右边数最后一条

    ## % :   从右边数第一条,从左边数最后一条

    将这一套全部解释透了。

    总结:

    取代或删除部分字符串

    只替换第一个对比符合的字符串

    语法:${变量/样式/替换字符串}

    替换全部符合的字符串

    语法:${变量//样式/替换字符串}

    注意,这里是不转义的。也就是不能用转义字符来转义,而是通过单引号来区分。例

    [root@localhost ~]# echo $filename  #假设我要将/换成,
    /usr/sbin/ntpdate
    [root@localhost ~]# r=${filename////,}  #开始的时候我采用的转义字符,//来表示/。结果,他会将//认成全部替换,剩余依然。
    [root@localhost ~]# echo $r
    ,usr,sbin,ntpdate
    [root@localhost ~]# r=${filename///,}   #继续测试,皆以失败告终
    [root@localhost ~]# echo $r          
    /usr/sbin/ntpdate
    [root@localhost ~]# r=${filename/ //,}
    [root@localhost ~]# echo $r           
    /usr/sbin/ntpdate
    [root@localhost ~]# r=${filename/ ///,}
    [root@localhost ~]# echo $r            
    /usr/sbin/ntpdate
    [root@localhost ~]# r=${filename////,} 
    [root@localhost ~]# echo $r           
    ,usr,sbin,ntpdate
    [root@localhost ~]# r=${filename/// /,}
    [root@localhost ~]# echo $r            
    /usr/sbin/ntpdate
    [root@localhost ~]# r=${filename/'/'/,}  #最后突然想用一下引号试试,结果成功。。。。
    [root@localhost ~]# echo $r            
    ,usr/sbin/ntpdate

    把对比符合的字符串删除

    只删一个

    语法:${变量/样式/}

    删除第一个符合样式的字符串,这里我理解的是,将符合条件的内容替换成空,即为删除

    要求匹配的条件在句首或者句尾

    匹配条件时,如果样式前加#,则该条件的要求要出现在变量值的开头才匹配

          如果样式前加%,则该条件要出现在变量值的末尾才匹配

    总结:

     四   变量扩展:取变量名列表,数组索引列表

    1   取变量名列表

    语法:${!开头字符串@}或${!开头字符串*}

    解析:把所有以制定字符串开头的变量名列出,用$IFS定义的第一个分隔符隔开。通常为  空格 

    [root@localhost ~]# echo ${!r@}
    r
    [root@localhost ~]# rr=
    [root@localhost ~]# echo ${!r@}
    r rr

    2  取数组索引

    语法:${!数组变量[@]}或${!数组变量[*]}  这两个语法尊选   @和*的区别

    解析:把数组变量所有的索引列出,各索引值之间,用$IFS定义第一个分隔字符。通常为 空格

    五   命令替换

    命令替换含义:是将命令执行后的标准输出放入变量中的功能。

    语法:  变量名称=$(命令)

    [root@localhost ~]# filename="/etc/passwd"
    [root@localhost ~]# IFS=' '
    [root@localhost ~]# r=$(cat $filename)
    [root@localhost ~]# echo $r
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    adm:x:3:4:adm:/var/adm:/sbin/nologin
    lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
    sync:x:5:0:sync:/sbin:/bin/sync
    shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
    halt:x:7:0:halt:/sbin:/sbin/halt

    多道命令:命令中间用  ;  号隔开,命令替换的值是最后一道指令执行的结果

    [root@localhost ~]# PDIR=$(cd ..;pwd)  #cd会执行,但是会将pwd的结果放到变量中
    [root@localhost ~]# echo $PDIR
    /
    [root@localhost ~]# pwd
    /root

    命令替换中,可以包含其他命令替换

    [root@localhost ~]# r=$(du -s $(cd ShellScript;pwd))
    [root@localhost ~]# echo $r
    48      /root/ShellScript
    [root@localhost ShellScript]# vim getipl.sh
    #取得IP地址
    #! /bin/bash
    tmp=$(ifconfig ens33 | grep 'inet')
    r=${tmp/inet /}
    ip=${r/ netmask*/}
    echo $ip
    ~                                                                                                                                                         
    ~                                                                               
    "getipl.sh" 5L, 93C 已写入                                    
    [root@localhost ShellScript]# sh getipl.sh 
    192.168.132.16

    六  算术扩展

    缘由:BASH中,变量没有数据类型,会将所有变量值以字符串的形式保存下来。因此任何关于运算的内容,都会原封不动按照字符串的形式显示出来。所以才出现了算术扩展的形式

    语法:$((变量A+变量B))

    [root@localhost ShellScript]# r=$((8+16))
    [root@localhost ShellScript]# echo $r
    24
  • 相关阅读:
    #特征方程,dp,快速幂#洛谷 4451 [国家集训队]整数的lqp拆分
    #状压dp,贪心#CF1316E Team Building
    #线段树,欧拉函数#CF1114F Please, another Queries on Array?
    #启发式合并,链表#洛谷 3201 [HNOI2009] 梦幻布丁
    #树状数组#洛谷 4113 [HEOI2012]采花
    #链表#洛谷 3794 签到题IV
    #矩阵乘法,斐波那契#洛谷 2544 [AHOI2004] 数字迷阵
    #dp#洛谷 4399 [JSOI2008]Blue Mary的职员分配
    #同余最短路#洛谷 3403 跳楼机
    #网络流,分层图#洛谷 4400 [JSOI2008] Blue Mary的旅行
  • 原文地址:https://www.cnblogs.com/Lonelychampion/p/11835655.html
Copyright © 2011-2022 走看看