Concaténation de chaînes bash utilisée pour construire la liste des paramètres

12

Compte tenu de ce morceau de bash:

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo $PARMS
rsync ${PARMS} . ${TARGET}

L'écho montre la chaîne PARMS comme prévu, aucune erreur ne s'affiche, mais rsync agit silencieusement comme si les options ajoutées par le + = n'existaient pas. Cependant, cela fonctionne comme prévu:

PARMS='-rvu'
rsync ${PARMS} --delete --exclude='.git' . ${TARGET}

Je suppose que j'ai foiré quelque chose avec des guillemets bash (toujours eu des problèmes avec ceux-ci), mais je ne sais pas exactement quoi et pourquoi les options sont ignorées même si la chaîne semble avoir été construite correctement.

neuviemeporte
la source
1
echo "$PARMS"et rsync "${PARMS}"...
jasonwryan
Cela fonctionne pour moi avec la bashversion 4.2.25 sans aucun changement.
Anthon

Réponses:

17

Il y a une différence entre:

PARMS+="... --exclude='.git'"

et

... --exclude='.git'

Dans le premier, les guillemets simples sont à l'intérieur des guillemets eux-mêmes, ils sont donc littéralement présents dans le texte substitué donné rsynccomme arguments. rsyncobtient un argument dont la valeur est --exclude='.git'. Dans le second, les guillemets simples sont interprétés par le shell au moment où ils sont écrits, car ils ne sont pas à l' intérieur des guillemets eux-mêmes et peuventrsync voir --exclude=.git.

Dans ce cas, vous n'avez pas du tout besoin de guillemets simples - .gitest un mot shell parfaitement valide en lui-même, sans caractères spéciaux, vous pouvez donc l'utiliser littéralement dans la commande.

Mieux pour ce genre de chose, cependant, est un tableau :

PARMS=(-rvu)
PARMS+=(--delete --exclude='.git')
rsync "${PARMS[@]}"

Cela crée votre commande en tant que mots séparés, avec les guillemets que vous souhaitez interpréter au moment où vous écrivez la ligne du tableau. "${PARMS[@]}"s'étend à chaque entrée du tableau en tant qu'argument distinct, même si l'argument lui-même contient des caractères spéciaux ou des espaces, rsyncvoit donc ce que vous avez écrit tel que vous le vouliez.

Michael Homer
la source
basheffectué le fractionnement de mots après a ${PARMS}été développé. Ainsi, la citation unique a également été interprétée par le shell.
cuonglm
2
Essayez! J'ai fait. Les guillemets restent, et s'il y avait des espaces entre eux, ils sont quand même des points de partage.
Michael Homer
@Gnouc: A partir de la page man bash: « Citation Suppression: Une fois les extensions précédentes, toutes les occurrences non cotées des personnages \ , 'et "qui ne résulte pas d'une des extensions ci - dessus sont supprimés. » "extensions ci-dessus" inclut l'extension des paramètres qui effectue l'extension de ${PARMS}.
camh
Merci. Donc, je comprends que dans ce cas, l'omission des guillemets simples à l'intérieur des doubles fonctionnera mais pour des raisons d'exhaustivité - et si j'avais besoin de citer des caractères spéciaux et que je ne voulais pas utiliser votre dernière approche?
neuviemeporte
Si vos caractères spéciaux ne font pas partie IFS(généralement des espaces), vous n'avez pas besoin de les citer. S'ils le sont, vous n'avez pas de chance, sauf si vous piratez quelque chose avec eval- c'est un peu une erreur en général, et les tableaux sont la bonne façon de gérer cela.
Michael Homer
2

En plus de la réponse de @Michael Homer , vous pouvez utiliser la bash fonction eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
cuonglm
la source
2
Vous pouvez, mais vous ne devriez pas. Des tableaux ont été ajoutés spécifiquement pour éviter cette utilisation de eval.
chepner