bash 变量替换不适用于 Solaris
bash variable substitution not working on Solaris
我在几个 Linux 盒子上有这个代码片段 运行,还有一个带有 bash 3.6 (iirc) 的 Solaris 10 盒子。但是,在 Solaris 11 机器上,使用 GNU bash, version 4.4.11(1)-release (sparc-sun-solaris2.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
,那么我将使用另一段代码。
影响此的更改发生在 Bash 4.3 和 Bash 4.4 中。观察:
Bash4.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}
在所有版本中都被视为 "bad substitution",即使没有 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]:-}
不会触发未设置的投诉
- 分别处理空字符串和 non-empty 字符串的情况
或者,如果 f ()
不是简化,并且您从不访问显示内容以外的元素,则可以进一步简化为
f () {
if [[ -z ${2:-} ]]; then
echo 0
else
echo $(( ${#2} - $(clean_count "") ))
fi
}
我在几个 Linux 盒子上有这个代码片段 运行,还有一个带有 bash 3.6 (iirc) 的 Solaris 10 盒子。但是,在 Solaris 11 机器上,使用 GNU bash, version 4.4.11(1)-release (sparc-sun-solaris2.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
,那么我将使用另一段代码。
影响此的更改发生在 Bash 4.3 和 Bash 4.4 中。观察:
Bash4.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}
在所有版本中都被视为 "bad substitution",即使没有 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]:-}
不会触发未设置的投诉
- 分别处理空字符串和 non-empty 字符串的情况
或者,如果 f ()
不是简化,并且您从不访问显示内容以外的元素,则可以进一步简化为
f () {
if [[ -z ${2:-} ]]; then
echo 0
else
echo $(( ${#2} - $(clean_count "") ))
fi
}