У меня есть древний сценарий bash, который я хотел бы улучшить. В этом скрипте используется много переменных, и в некоторых случаях есть ссылки на некоторые из них, но они пусты или не установлены. Обычно это не проблема, потому что сценарий изначально создавался таким образом.
Однако я начал использовать Shellcheck который часто жалуется на пропущенные цитаты. Итак, я добавил цитаты и обнаружил неудобство, для которого у меня нет хорошего решения.
Есть много строк, которые выглядят так:
cmd $param1 $param2 $param3 $param4
Если один из параметров пуст или не установлен, он все еще работает, и количество параметров, которые получает cmd, уменьшается. Но если я начну добавлять цитаты
cmd "$param1" "$param2" "$param3" "$param4"
cmd всегда будет получать 4 параметра, независимо от того, пуст ли какой-либо из них.
Почему это так, совершенно ясно. Обходной путь также ясен (проверка пустоты перед использованием параметра), но он уродлив и требует большого количества дополнительного кода.
Итак, я ищу умное решение, которое либо а) пропускает параметр (включая кавычки!), Если он пуст, б) добавляет цитату только для непустых переменных
Какую оболочку вы используете для этого скрипта? Я полагаю, это / bin / sh
Единственная сущность, подобная массиву, в оболочке POSIX - это позиционные параметры. Вы могли сделать это:
set -- # clear the positional parameters
for param in "$param1" "$param2" "$param3" "$param4"; do
if [ -n "$param" ]; then
# append this param to the positional params
set -- "$@" "$param"
fi
done
# invoke the command with the positional params, properly quoted
cmd "$@"
Если вы хотите поумнее, оберните вызов cmd в функцию:
invoke_cmd() {
n="$#" # remember the original number of params
# append to the args list only if non-empty
for arg; do [ -n "$arg" ] && set -- "$@" "$arg"; done
# discard the original params (including the empty ones)
shift "$n"
# and invoke the cmd
cmd "$@"
}
invoke_cmd "$param1" "$param2" "$param3" "$param4"
Для реального bash упростите
invoke_cmd() {
local args=()
for arg; do [[ "$arg" ]] && args+=("$arg"); done
cmd "${args[@]}"
}
invoke_cmd "$param1" "$param2" "$param3" "$param4"
Другой вариант (помимо того, что описал @glennjackman) - использовать условное расширение с :+
включить (правильно заключенную) переменную, только если она установлена и не пуста:
cmd ${param1:+"$param1"} ${param2:+"$param2"} ${param3:+"$param3"} ${param4:+"$param4"}
(Примечание: условное расширение находится в posix specв разделе 2.6.2; но я не совсем уверен, что все оболочки posix будут должным образом учитывать двойные кавычки в альтернативном значении.)
Если вы хотите внести в скрипт более обширные изменения и ограничить его до bash (т.е. если вы используете #!/bin/bash
или #!/usr/bin/env bash
shebang), как правило, проще накапливать параметры в массиве:
cmd_parameters=() # start with an empty array
if [ something ]; then
cmd_parameters+=("$param1") # Add an element -- note that the () are critical here!
fi
if [ something_else ]; then
cmd_parameters+=("$param2")
fi
...etc
cmd "${cmd_parameters[@]}"
Обратите внимание, что эти подходы вполне можно комбинировать, в том числе используя :+
трюк с условным добавлением элемента массива:
cmd_parameters+=(${param3:+"$param3"})
и / или смешивание массива и условных параметров в одной команде:
cmd "${cmd_parameters[@]}" ${param4:+"$param4"}
Кроме того, играя на предложении @ glennjackman функции-оболочки, можно было бы написать общую функцию-оболочку:
conditional_args() {
# invoke a command ($1) with any empty arguments omitted
args=()
for arg; do
args+=(${arg:+"$arg"})
done
"${args[@]}" # Note that the first "arg" is actually the command itself
}
conditional_args cmd "$param1" "$param2" "$param3" "$param4"