Como posso expandir uma variável entre aspas para nada se estiver vazia?

15

Digamos que eu tenha um script fazendo:

some-command "$var1" "$var2" ...

E, no caso de var1 estar vazio, prefiro que seja substituído por nada em vez da string vazia, para que o comando executado seja:

some-command "$var2" ...

e não:

some-command '' "$var2" ...

Existe uma maneira mais simples do que testar a variável e incluí-la condicionalmente?

if [ -n "$1" ]; then
    some-command "$var1" "$var2" ...
    # or some variant using arrays to build the command
    # args+=("$var1")
else
    some-command "$var2" ...
fi

Existe uma substituição de parâmetro que pode expandir para nada em bash, zsh ou algo semelhante? Eu ainda posso querer usar globbing no restante dos argumentos, então desabilitar isso e desmarcar a variável não é uma opção.

    
por muru 10.01.2018 / 07:44

3 respostas

23

conchas compatíveis com Posix e Bash tem ${parameter:+word} :

If parameter is unset or null, null shall be substituted; otherwise, the expansion of word (or an empty string if word is omitted) shall be substituted.

Então você pode fazer:

${var1:+"$var1"}

e ter var1 verificado, e "$var1" ser usado se estiver definido e não vazio (com as regras comuns de aspas duplas). Caso contrário, ele se expande para nada. Note que somente a parte inner é citada aqui, não a coisa toda.

O mesmo também funciona em zsh. Você tem que repetir a variável, então não é o ideal, mas funciona exatamente como você queria.

Se você quiser que uma variável definida-mas-vazia se expanda para um argumento vazio, use ${var1+"$var1"} .

    
por 10.01.2018 / 07:51
3

Isso é o que o zsh faz por padrão quando você omite as aspas:

some-command $var1 $var2

Na verdade, a única razão pela qual você ainda precisa de cotações em zsh em torno da expansão de parâmetro é evitar esse comportamento (a remoção vazia), pois zsh não tem os outros problemas que afetam outros shells quando você não cita expansões de parâmetros (o split + glob implícito) .

Você pode fazer o mesmo com outros shells do tipo POSIX se desativar o split e o glob:

(IFS=; set -o noglob; some-command $var1 $var2)

Agora, eu diria que, se sua variável pode ter valor 0 ou 1, ela deve ser uma matriz e não uma variável escalar e usar:

some-command "${var1[@]}" "${var2[@]}"

E use var1=(value) quando var1 deve conter um valor, var1=('') quando for conter um valor vazio e var1=() quando for conter o valor no .

    
por 11.01.2018 / 23:05
0

Corri para isso usando o rsync em um script bash que iniciou o comando com ou sem -n para alternar execuções secas. Acontece que o rsync e um número de comandos do gnu tomam '' como um primeiro argumento válido e agem de maneira diferente do que se ele não estivesse lá.

Isso levou bastante tempo para depurar, já que os parâmetros nulos são quase completamente invisíveis.

Alguém na lista de rsync me mostrou uma maneira de evitar esse problema, ao mesmo tempo em que muito simplificava minha codificação. Se bem entendi, esta é uma variação da última sugestão de @ Stéphane Chazelas.

Crie seus argumentos de comando em várias variáveis separadas. Eles podem ser definidos em qualquer ordem ou lógica que seja adequada ao problema.

Depois, no final, use as variáveis para construir um array com tudo em seu devido lugar e use isso como argumentos para o comando atual.

Desta forma, o comando só é emitido em um lugar no código, em vez de ser repetido para cada variação dos argumentos.

Qualquer variável vazia desaparece usando este método.

Eu sei que usar o eval é muito mal visto. Não me lembro de todos os detalhes, mas pareci precisar dele para fazer as coisas funcionarem dessa maneira - algo a ver com a manipulação de parâmetros com espaço em branco incorporado.

Exemplo:

dry_run=''
if [[ it is a test run ]]
then
  dry_run='-n'
fi
...
rsync_options=(
  ${dry_run}
  -avushi
  ${delete}
  ${excludes}
  --stats
  --progress
)
...
eval rsync "${rsync_options[@]}" ...
    
por 13.01.2018 / 05:52