• Bash编程(4) 参数与变量


    1. 变量命名

    变量命名只能使用数字、下划线、字母,且仅能以下划线或字母开头。

    变量很少使用单个字母,单个字母一般用于循环或读取一次性文件的时候。

    例:

    while IFS=: read login a b c name e
    do
        printf "%-12s %s
    " "$login" "$name"
    done < /etc/passwd 

    变量名最好能望名知意

    2. 变量作用域

    若脚本a调用脚本b,则a无法得知b中的变量,除非将b中的变量写入环境变量中。

    脚本中在变量前使用export内置命令,则可以将该变量设置为环境变量。

    如:

    var=whatever
    export var
    
    # bash中可简写为export var=whatever 

    导出(export)一个变量并不是在任何地方都可以访问该变量,除了子进程外。

    例:showvar用于检验变量是否被设置

    if [[ ${x+Z} = Z ]] ## $x被设置
    then
        echo ${x+Z}
        echo Z
        if [[ -n $x ]] ## $x非空
        then
            printf " $x = %s
    " "$x"
        else
            printf " $x is set but empty
    "
        fi
    else
        printf " %s is not set
    " "$x"
    fi 

    若导出了一个变量,其一直保留在环境中,除非直接unset命令。

    $ unset x
    $ mv variable showvar
    $ ./showvar 
     $x is not set
    $ x=3
    $ ./showvar 
     $x is not set
    $ export x=4
    $ ./showvar 
     $x = 4
    $ x=  ## bash中,对一个变量重新赋值,并不会从环境变量中移除该变量
    $ ./showvar 
     $x is set but empty 

    设置在子shell中的变量对调用它的脚本不可见。子shell包含命令替换,如$(command) 或`command`; 管道中的所有元素;括号中的内容,如( command )

    例:

    printf "%s
    " ${RANDOM}{,,,,,} |
        while read num
        do
            (( num > ${biggest:=0} )) && biggest=$num
        done
    printf "The largest number is: %d
    " "$biggest" 

    执行该脚本,发现biggest为0,原因在于循环是管道的一部分,将会在子shell中执行它,而子shell中的变量对该执行脚本不可见。

    bash4.2中,新的选项lastpipe,允许管道中最后的进程在当前shell中执行,通过调用:shopt -s lastpipe

    3. Shell变量

    shell自带变量BASH_VERSION表示bash的版本。

    例:

    case $BASE_VERSION in [12].*) echo "You need at least bash3.0 to run this script" >&2; exit 2;;
    esac 

    提示符PS1,PS2用于命令行中的shell交互时,PS3在使用内置命令select时使用,PS4用于在执行跟踪模式下,每行之前的打印。

    Shell变量包含:

    BASH BASHOPTS BASHPID BASH_ALIASES
    BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND
    BASH_EXECUTION_STRING BASH_LINENO BASH_REMATCH BASH_SOURCE
    BASH_SUBSHELL BASH_VERSINFO BASH_VERSION COMP_CWORD
    COMP_KEY COMP_LINE COMP_POINT COMP_TYPE
    COMP_WORDBREAKS COMP_WORDS COPROC DIRSTACK
    EUID FUNCNAME GROUPS HISTCMD
    HOSTNAME HOSTTYPE LINENO MACHTYPE
    MAPFILE OLDPWD OPTARG OPTIND
    OSTYPE PIPESTATUS PPID PWD
    RANDOM READLINE_LINE READLINE_POINT REPLY
    SECONDS SHELLOPTS SHLVL UID
    BASH_COMPAT BASH_ENV BASH_XTRACEFD CDPATH
    CHILD_MAX COLUMNS COMPREPLY EMACS
    FCEDIT FIGNORE FUNCNEST GLOBIGNORE
    HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE
    HISTSIZE HISTTIMEFORMAT HOME HOSTFILE
    IFS IGNOREEOF INPUTRC LANG
    LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES
    LC_NUMERIC LC_NUMERIC LINES MAIL
    MAILCHECK MAILPATH OPTERR PATH
    POSIXLY_CORRECT PROMPT_COMMAND PROMPT_DIRTRIM PS1
    PS2 PS3 PS4 SHELL
    TIMEFORMAT TMOUT TMPDIR auto_resume
    histchars 

    4. 参数扩展

    (1) ${var:-default}和${var-default}:使用默认值

    ## ${var:-default}用于检查变量未被设置或为空,若为空或未被设置,则使用默认值
    $ var= $ ./sa ${var:-default} :default: ## 如果取掉":",则${var-default}仅检测变量是否为unset $ ./sa ${var-default} :: $ unset var $ ./sa ${var-default} :default: # 为filename设置默认值 defaultfile=$HOME/.bashrc ## parse options here filename=${filename:-"$defaultfile"

    (2) ${var:+altername}, ${var+alternate}: 使用替换值

    ## $var设置但为空,因此替换值不生效
    $ var=
    $ ./sa "${var:+alternate}"
    ::
    
    ## $var被设置,且不为空,则替换值生效
    $ ./sa ${var:+alternate}
    :alternate:
    
    ## 取消冒号后,即使变量为空,只要被设置,替换值将生效
    $ var=
    $ ./sa "${var+alternate}"  ## $var被设置
    :alternate:
    $ unset var
    $ ./sa "${var+alternate}" ## $var未被设置
    ::
    $ var=value
    $ ./sa "${var+alternate}" ## $var被设置且非空
    :alternate: 

    常用于变量中增加字符串。例:

    ## 变量为空时,不想在变量中增加分隔符
    $ var=
    $ for n in a b c d e f g
    > do
    >     var="$var $n"
    > done
    $ ./sa "$var"
    : a b c d e f g:
    
    # 为了防止a前面的空格,可以使用参数扩展
    $ var=
    $ for n in a b c d e f g
    > do
    >    var="${var:+"$var "}$n" # $var被设置且非空时,则替换为"$var "
    > done
    $ ./sa "$var"
    :a b c d e f g:
    
    # 上面的方法是如下2个方法方法的简写
    # 1
    if [ -n "$var" ]
    then
        var="$var $n"
    else
        var=$n
    fi
    # 2
    [ -n "$var" ] && var="$var $n" || var=$n 

    (3) ${var:=default}, ${var=default}: 赋初始值

    ${var:=default}与${var:-default}类似,不同之处在于它也向变量赋初始值。

    $ unset n
    $ while :
    > do
    >     echo :$n:
    >     [ ${n:=0} -gt 3 ] && break ## 如果未被设置或为空,则将$n设为0
    >     n=$(( $n + 1 ))
    > done
    ::
    :1:
    :2:
    :3:
    :4

     (4) ${var:?message}, ${var?message}: 如果为空或未被设置,则显示错误信息到标准错误输出

    $ ./checkarg 
    ./checkarg: line 2: 1: An argument is required
    $ ./checkarg x
    ./checkarg: line 2: 2:  Two arguments are required
    $ ./checkarg '' ''
    ./checkarg: line 5: 1:  A non-empty argument is required
    $ ./checkarg x ''
    ./checkarg: line 5: 2: Two non-empty arguments are required
    $ ./checkarg x x
    Thank you. 

    (5) ${#var}: 变量内容的长度

    read passwd
    if [ ${#passwd} -lt 8 ]
    then
        printf "Password is too short: %d characters
    " "$#" >&2
        exit 1
    fi 

    (6)  ${var%PATTERN}: 从变量结尾移除最短匹配

    $ var=Toronto
    $ var=${var%o*}
    $ printf "%s
    " "$var"
    Toront
    $ printf "%s
    " "${var%o*}"
    Tor
    
    # dname, 打印文件路径的部分
    case $1 in
        */*) printf "%s
    " "${1%/*}" ;;
        *) [ -e "$1" ] && printf "%s
    " "$PWD" || echo "." ;;
    esac
    
    $ ./dname /etc/passwd
    /etc
    $ ./dname bin
    . 

    (7) ${var%%PATTERN}: 从变量结尾移除最长匹配

    $ var=Toronto
    music@ds01:~/songwang4/test$ ./sa "${var%%o*}"
    :T: 

    (8) ${var#PATTERN}: 从变量起始处移除最短匹配

    $ var=Toronto
    $ ./sa "${var#*o}"
    :ronto: 

    (9) ${var##PATTERN}: 从变量起始处移除最长匹配

    scriptname=${0##*/} ## /home/chris/bin/script => script 

    (10) ${var//PATTERN/STRING}: 使用STRING替换所有的PATTERN实例

    ## 密码隐藏显示
    $ passwd=zx01.=+-a
    $ printf "%s
    " "${passwd//?/*}"
    *********
    
    ## 单斜杠"/"仅替换第一个匹配的字符
    $ printf "%s
    " "${passwd/a/*}"
    zx01.=+-* 

    (11) ${var:OFFSET:LENGTH}: 返回$var的子串

    OFFSET表示起始位置,LENGTH表示截取长度。

    $ var=Toronto
    $ ./sa "${var:3:2}"
    :on:
    $ ./sa "${var:3}"
    :onto:
    
    ## 负数表示从字符串结尾开始
    $ ./sa "${var: -3}"  ## 注意-3前面的空格,防止与变量默认值扩展混淆
    :nto: 

    (12) ${!var}: 间接引用

    若变量中含有另一个变量的名称,bash可以使用间接引用。

    $ x=yes
    $ a=x
    $ ./sa ${!a}
    :yes:
    
    ## 使用eval也可实现类似功能
    $ eval "./sa $$a"
    :yes: 

     (13) ${var^PATTERN}: 大写转换

    如果匹配PATTERN,则var的第一个字符转换为大写;当为^^时,匹配到PATTERN的所有字符转换为大写;如果PATTERN为空,则所有都匹配。

    $ var=toronto
    $ ./sa "${var^}" ## PATTERN为空且为^,时,第一个字符大写转换
    :Toronto:
    $ ./sa "${var^[n-z]}"
    :Toronto:
    
    $ ./sa "${var^^[a-o]}" ## 匹配从a到o的所有字符
    :tOrONtO
    $ ./sa "${var^^}"  ## 匹配所有
    :TORONTO: 

    (14) ${var,PATTERN}:小写转换

    与${var^PATTERN}用法相同,区别在于小写转换

    $ var=TORONTO
    $ ./sa "${var,,}"
    :toronto:
    $ ./sa "${var,,[N-Q]}"
    :ToRonTo: 

     (15) ${var~}:大小写取反

    $ var=Toronto
    $ ./sa "${var~}"
    :toronto:
    $ ./sa "${var~~}"
    :tORONTO: 

     5. 位置参数

    位置参数由$1,...${10}引用,全部参数可以使用"$@"或"$*",大于9的索引参数需要以大括号封装。

    不带参数的shift命令将会移除第一个参数,并将剩余的参数前移一位,如历史的$2将会变为$1。带参数的shift将会移动指定的大小。

    $ shift 3 ## 移除前3个参数
    $ shift "$#" ## 移除所有参数
    $ shift "$(( $# -2 ))"  ## 保留最后2位参数
    
    ## 使用每一个参数,可以通过如下2种方法
    ## 1. 使用$@
     for param in "#@"  ## 或 for param
    do
        : do something with $param
    done
    
    ## 2. 使用shift
    while (( $# ))
    do
        : do something with $1
        shift
    done 

    6. 数组

    (1) 整型索引数组

    数组元素索引从0开始。Bash中的数组是稀疏的,即索引号可以不连续。

    # BASH_VERSINFO表示当前运行shell的版本信息,第一个元素表示主版本号,第二个表示次版本号
    $ printf "%s
    " "${BASH_VERSINFO[0]}"
    4
    $ printf "%s
    " "${BASH_VERSINFO[1]}"
    3

    可以使用*和@打印数组所有元素。当使用"*"且数组被引号括起,则数组元素不会拆分,若没有引号则会被拆分;当使用"@"时,均会被拆分

    $ printf "%s
    " "${BASH_VERSINFO[*]}" # 不拆分
    4 3 48 1 release x86_64-pc-linux-gnu
    $ printf "%s
    " ${BASH_VERSINFO[*]} # 拆分
    4
    3
    48
    1
    release
    x86_64-pc-linux-gnu
    $ printf "%s
    " ${BASH_VERSINFO[@]} # 使用@时,默认均拆分,对仅限于数组元素间的拆分,而不会涉及到元素内容
    4
    3
    48
    1
    release
    x86_64-pc-linux-gnu
    
    ## 提取数组中第二个和第三个元素
    $ printf "%s
    " "${BASH_VERSINFO[@]:1:2}"
    3
    48
    
    ## 当使用*或@时,"#"返回数组长度,如果指定数组元素时,则返回数组元素的长度
    $ printf "%s
    " "${#BASH_VERSINFO[*]}"
    6
    $ printf "%s
    " "${#BASH_VERSINFO[2]}" "${#BASH_VERSINFO[5]}"
    2
    19
    
    ## 数组通过下标赋值
    $ name[0]=1
    $ name[42]=2
    
    ## 非连续下标的好处,原因在于对它们的操作将变得简单,直接取下一个未被设置的元素即可
    $ unset a
    $ a[${#a[@]}]="1 $RANDOM"
    $ a[${#a[@]}]="2 $RANDOM"
    $ a[${#a[@]}]="3 $RANDOM"
    $ a[${#a[@]}]="4 $RANDOM"
    $ printf "%s
    " "${a[@]}"
    1 20457
    2 19494
    3 30222
    4 19320
    
    ## 数组也可以仅使用如下一个命令生成
    $ province=( Quebec Ontario Manitoba )
    $ printf "%s
    " "${province[@]}"
    Quebec
    Ontario
    Manitoba
    
    ## +=用于在数组索引末位添加值
    $ province+=( Saskatchewan )
    $ province+=( Alberta "British Columbia" "Nova Scotia" )
    $ printf "%-25s %-25s %s
    " ${province[@]}
    Quebec                    Ontario                   Manitoba
    Saskatchewan              Alberta                   British
    Columbia                  Nova                      Scotia
    $ printf "%-25s %-25s %s
    " "${province[@]}"
    Quebec                    Ontario                   Manitoba
    Saskatchewan              Alberta                   British Columbia
    Nova Scotia 

    (2) 关联数组 

    bash 4后,允许使用字符串作为下标,且数组必须先声明。

    $ declare -A array
    $ for subscript in a b c d e
    > do
    >     array[$subscript]="$subscrupt $RANDOM"
    > done
    $ printf ":%s:
    " "${array["c"]}" ## 打印单个元素
    : 25475:
    $ printf ":%s:
    " "${array[@]}"  ## 打印整个数组
    : 26862:
    : 32278:
    : 25475:
    : 12284:
    : 22720
  • 相关阅读:
    JavaScript面试库
    JS事件委托的原理和应用
    缓存ABC
    网络模型探究
    持续集成配置之Nuget
    angular应用容器化部署
    微服务随想
    .NET性能优化小技巧
    博客园博客小优化
    Emmet 简介
  • 原文地址:https://www.cnblogs.com/mengrennwpu/p/10229685.html
Copyright © 2020-2023  润新知