bash变量替换不适用于Solaris

时间:2018-03-08 19:31:58

标签: bash solaris gnu substitution variable-expansion

我在几个Linux机器上运行此代码段,并在Solaris 10机箱上运行bash 3.6(iirc)。但是,在带有GNU bash, version 4.4.11(1)-release (sparc-sun-solaris2.11)的Solaris 11机器上,它会出现以下错误。

#!/bin/env bash
CLEAN_COUNT() {
    local L_STRING=$(sed '/[^[:graph:][:space:]]/{
        s/[^[:graph:][:space:]]//g; s/\[[0-9]*m//g; s/(B//g
        }' <<<$*) || return 1

    echo ${#L_STRING}
}

f() {
   ARGS=($@)
   echo $((${#ARGS[1]:-0} - $(CLEAN_COUNT ${ARGS[1]:-0}) ))
}

f one two three four

收到错误:./gather_data.bash: line 15: ${#ARGS[1]:-0} - $(CLEAN_COUNT ${ARGS[1]:-0}) : bad substitution

我已经在其自己的脚本中隔离了上述代码,我已经将该框中的shopt和set -o设置与另一个设置进行了比较。我很困惑。如果我可以在没有替换的情况下使代码工作,即使ARGS没有元素1而且我正在运行set -o nounset,那么我将使用另一段代码。

1 个答案:

答案 0 :(得分:2)

影响这种情况的变化发生在Bash 4.3和Bash 4.4中。观察:

  • Bash 4.2中没有错误:

    $ docker run --rm -it bash:4.2 bash -u
    bash-4.2$ bash --version | head -n 1
    GNU bash, version 4.2.53(2)-release (x86_64-pc-linux-musl)
    bash-4.2$ declare -a var && echo "${#var[1]:-1}"
    0
    

    但这实际上并没有打印我的默认值:var[1]是空字符串,因此是0-u似乎忽略了var没有元素。 echo "${#var[1]:-1}"echo "${#var[1]}"echo "${#var[1]}"之间的行为没有区别,它们都打印0

  • Bash 4.3抱怨未绑定的变量:

    $ docker run --rm -it bash:4.3 bash -u
    bash-4.3$ bash --version | head -n 1
    GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-musl)
    bash-4.3$ declare -a var && echo "${#var[1]:-1}"
    bash: var: unbound variable
    
  • Bash 4.4抱怨替换:

    $ docker run --rm -it bash:4.4 bash -u
    bash-4.4$ bash --version | head -n 1
    GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-musl)
    bash-4.4$ declare -a var && echo "${#var[1]:-1}"
    bash: ${#var[1]:-1}: bad substitution
    

    即使没有set -u

    bash-4.4# set +o nounset
    bash-4.4# declare -a var && echo "${#var[1]:-1}"
    bash: ${#var[1]:-1}: bad substitution
    

此外,${#var:-1}被视为&#34;不良替代&#34;在所有版本中,即使没有set -u

$ for v in 3.2 4.{0..4}; do docker run --rm -it bash:$v; done
bash-3.2# echo "${#var:-1}"
bash: ${#var:-1}: bad substitution
bash-3.2# exit
exit
bash-4.0# echo "${#var:-1}"
bash: ${#var:-1}: bad substitution
bash-4.0# exit
exit
bash-4.1# echo "${#var:-1}"
bash: ${#var:-1}: bad substitution
bash-4.1# exit
exit
bash-4.2# echo "${#var:-1}"
bash: ${#var:-1}: bad substitution
bash-4.2# exit
exit
bash-4.3# echo "${#var:-1}"
bash: ${#var:-1}: bad substitution
bash-4.3# exit
exit
bash-4.4# echo "${#var:-1}"
bash: ${#var:-1}: bad substitution
bash-4.4# exit
exit

我无法在NEWS中看到有关此行为的任何更改,但似乎有道理,因为${#var[0]:-1}默认情况下不会默认为1 ,所以现在行为在标量和数组之间是一致的。

这就是说,我按照以下方式重写你的功能:

f () {
    local args=("$@")
    if [[ -z ${args[1]:-} ]]; then
        echo 0
    else
        echo $(( ${#args[1]} - $(clean_count "${args[1]}") ))
    fi
}
  • 将大写变量名重命名为小写,以避免与shell和环境变量发生冲突
  • args本地功能
  • 在args中引用"$@"以避免在分配数组元素之前拆分
  • 检查args[1]是否为空字符串,确保${args[1]:-}
  • 未触发任何未设置的投诉
  • 分别处理空和非空字符串的案例

或者,如果f ()不是简化版,并且您永远不会访问您展示的元素以外的其他元素,则可以进一步简化为

f () {
    if [[ -z ${2:-} ]]; then
        echo 0
    else
        echo $(( ${#2} - $(clean_count "$2") ))
    fi
}