web-dev-qa-db-fra.com

Comment puis-je élargir une variable citée à rien si c'est vide?

Dis que j'ai un script faisant:

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

Et, dans le cas où var1 est vide, je préférerais qu'elle soit remplacée par rien au lieu de la chaîne vide, de sorte que la commande exécutée soit:

some-command "$var2" ...

et pas:

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

Y a-t-il un moyen plus simple que de tester la variable et conditionnellement, y compris?

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-t-il une substitution de paramètre que ne peut se développer à rien dans Bash, ZSH ou similaire? Je souhaiterais peut-être toujours utiliser le globbing dans le reste des arguments, alors désactivez cela et non craint que la variable n'est pas une option.

23
muru

coquilles compatibles POSIX et Bash ont${parameter:+Word}:

Si paramètre est déséquilibré ou null, NULL doit être substitué; Sinon, l'expansion de mot (ou une chaîne vide si mot est omis) doit être substitué.

Donc, vous pouvez juste faire:

${var1:+"$var1"}

et ont var1 être vérifié et "$var1" être utilisé s'il est défini et non vide (avec les règles de double citation ordinaires). Sinon, il ne se développe à rien. Notez que seul le interne est cité ici, pas le tout.

La même chose fonctionne aussi dans ZSH. Vous devez répéter la variable, donc ce n'est pas idéal, mais cela fonctionne exactement comme vous le souhaitez.

Si vous souhaitez une variable définie mais vide pour étendre à un argument vide, utilisez ${var1+"$var1"} au lieu.

31
Michael Homer

C'est ce que zsh fait par défaut lorsque vous omettez les devis:

some-command $var1 $var2

En fait, la seule raison pour laquelle vous avez toujours besoin de citations dans ZSH autour de l'expansion des paramètres est d'éviter ce comportement (la suppression vide) comme zsh _ n'a pas les autres problèmes qui affectent d'autres coquillages lorsque vous n'avez pas 'T C devis des extensions de paramètres (la division implicite + globe .

Vous pouvez faire la même chose avec d'autres obus de Posix-comme si vous désactivez Split et Glob:

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

Je dirais maintenant que si votre variable peut avoir une valeur 0 ou 1, il devrait s'agir d'une matrice et d'une variable scalaire et d'une utilisation:

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

Et utilisez var1=(value) lorsque var1 Doit contenir une valeur, var1=('') quand il doit contenir une valeur vide et var1=() quand il doit contenir Non valeur.

5
Stéphane Chazelas

J'ai rencontré cela en utilisant RSYNC dans un script Bash qui a démarré la commande avec ou sans -n Pour basculer les courses à sec. Il s'avère que rsync et un certain nombre de commandes GNU prennent '' En tant que premier argument valide et agissez différemment de celui-ci.

Cela a pris assez d'un certain temps pour déboguer alors que les paramètres nuls sont presque complètement invisibles.

Quelqu'un sur la liste Rsync m'a montré un moyen d'éviter ce problème tout en maintenant grandement Simplifier mon codage. Si je le comprends correctement, il s'agit d'une variation de la dernière suggestion de @ Stéphane Chazelas.

Construisez vos arguments de commande dans un certain nombre de variables distinctes. Ceux-ci peuvent être définis dans n'importe quel ordre ou logique qui convient au problème.

Ensuite, à la fin, utilisez les variables pour construire un tableau avec tout dans son endroit approprié et utilisez-le comme des arguments vers la commande réelle.

De cette façon, la commande n'est émise qu'à un endroit du code au lieu d'être répétée pour chaque variation des arguments.

Toute variable vide disparaît simplement en utilisant cette méthode.

Je sais que l'utilisation d'EVAL est fortement fronçée. Je ne me souviens pas de tous les détails, mais j'ai semblé en avoir besoin pour que les choses puissent fonctionner de cette façon - quelque chose à voir avec la manipulation des paramètres avec un espace blanc intégré.

Exemple:

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[@]}" ...
0
Joe