Verkettung von Bash-Strings zum Erstellen einer Parameterliste

12

Angesichts dieser Bash:

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

Das Echo zeigt die PARMS-Zeichenfolge wie erwartet an, es wird kein Fehler angezeigt, aber rsync verhält sich im Hintergrund so, als ob die mit + = hinzugefügten Optionen nicht vorhanden wären. Dies funktioniert jedoch wie erwartet:

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

Ich glaube, ich habe etwas mit Bash-Anführungszeichen vermasselt (hatte immer Probleme damit), bin mir aber nicht ganz sicher, was und warum die Optionen ignoriert werden, obwohl die Zeichenfolge anscheinend korrekt erstellt wurde.

neuviemeporte
quelle
1
echo "$PARMS"und rsync "${PARMS}"...
Jasonwryan
Dies funktioniert bei mir mit der bashVersion 4.2.25 ohne Änderungen.
Anthon

Antworten:

17

Es gibt einen Unterschied zwischen:

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

und

... --exclude='.git'

Im ersten Fall handelt es sich bei den einfachen Anführungszeichen um innere Anführungszeichen, sodass sie im ersetzten Text, der rsyncals Argumente angegeben wird, buchstäblich vorkommen . rsyncbekommt ein Argument dessen Wert ist --exclude='.git'. In der zweiten werden die einzelnen Anführungszeichen von der Shell zum Zeitpunkt des Schreibens interpretiert, da sie nicht in Anführungszeichen stehen und rsynczu sehen sind --exclude=.git.

In diesem Fall brauchen Sie die einfachen Anführungszeichen überhaupt nicht - es .githandelt sich um ein vollständig gültiges Shell-Wort ohne Sonderzeichen, sodass Sie es wörtlich im Befehl verwenden können.

Besser für diese Art von Dingen ist jedoch ein Array :

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

Dies baut Ihren Befehl als separate Wörter auf, wobei die zu interpretierenden Anführungszeichen zum Zeitpunkt des Schreibens der Array-Zeile verwendet werden. "${PARMS[@]}"Erweitert jeden Eintrag im Array als separates Argument, auch wenn das Argument selbst Sonderzeichen oder Leerzeichen enthält. Sie sehen also rsync, was Sie geschrieben haben, wie Sie es gemeint haben.

Michael Homer
quelle
bashWortteilung durchgeführt, nachdem ${PARMS}erweitert wurde. Das einfache Anführungszeichen wurde also auch von der Shell interpretiert.
Dienstag,
2
Versuch es! Ich tat. Die Anführungszeichen bleiben erhalten, und wenn sich Leerzeichen dazwischen befanden, sind sie ohnehin Teilungspunkte.
Michael Homer
@Gnouc: Von der bash man - Seite: „Quote Ausbau: Nach den vorangegangenen Erweiterungen, alle unquoted Vorkommen der Zeichen \ , 'und ". , Die nicht aus einem der oben genannten Erweiterungen geführt haben entfernt werden“ "über Erweiterungen" beinhaltet Parametererweiterung, die die Erweiterung von ausführt ${PARMS}.
Camh
Vielen Dank. Ich verstehe also, dass in diesem Fall das Weglassen der einfachen Anführungszeichen in den doppelten der Vollständigkeit halber funktionieren wird - was wäre, wenn ich einige Sonderzeichen zitieren müsste und Ihren letzteren Ansatz nicht verwenden wollte?
Neuviemeporte
Wenn Ihre Sonderzeichen nicht Teil von IFS(im Allgemeinen Leerzeichen) sind, müssen Sie sie nicht in Anführungszeichen setzen. Wenn evaldies der Fall ist , haben Sie Pech, es sei denn, Sie hacken etwas zusammen mit - dies ist im Allgemeinen ein Misserfolg, und Arrays sind der richtige Weg, damit umzugehen.
Michael Homer
2

Neben @ Michael Homers Antwort , können Sie bash Funktion eval :

PARMS='-rvu'
PARMS+=" --delete --exclude='.git'"
echo "$PARMS"
eval "rsync ${PARMS} . "'"${TARGET}"'
cuonglm
quelle
2
Sie können, aber Sie sollten nicht. Arrays wurden speziell hinzugefügt , um diese Verwendung von zu vermeiden eval.
Chepner