Подстановка переменных bash не работает в Solaris
У меня есть этот фрагмент кода, работающий на нескольких Linux-блоках, и Solaris 10 с bash 3.6 (iirc). Тем не менее, на коробке 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
Я выделил приведенный выше код в своем собственном скрипте, я сравнил set -o
Настройки на этом поле с другим. Я в замешательстве. Если я смогу заставить код работать без подстановки, даже если у ARGS нет элемента 1 и я работаю set -o nounset
тогда я буду использовать другой кусок кода.
1 ответ
Изменения, влияющие на это, произошли в 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}
считается "плохой заменой" во всех версиях, даже без 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
}
- Переименуйте имена переменных в верхнем регистре в строчные, чтобы избежать конфликта с переменными оболочки и среды
- Делать
args
местный, чтобы функционировать - котировка
"$@"
в аргументах, чтобы избежать разбиения перед назначением элементов массива - Проверить, если
args[1]
пустая строка, убедитесь, что не запущена жалоба${args[1]:-}
- Лечить случаи для пустой и непустой строки отдельно
В качестве альтернативы, если f ()
это не упрощение, и вы никогда не получите доступ к элементам, кроме того, что вы показываете, вы могли бы еще больше упростить
f () {
if [[ -z ${2:-} ]]; then
echo 0
else
echo $(( ${#2} - $(clean_count "$2") ))
fi
}