Назад | Перейти на главную страницу

цитировать, только если переменная не пуста

У меня есть древний сценарий 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"