Was ist der richtige Weg, um $ (Befehl $ arg) zu zitieren?

17

Es ist höchste Zeit, dieses Rätsel zu lösen, das mich seit Jahren beschäftigt ...

Ich habe dies von Zeit zu Zeit getroffen und dachte, das ist der richtige Weg:

$(comm "$(arg)")

Und dachte, meine Ansicht wurde stark von der Erfahrung gestützt. Aber ich bin mir nicht mehr so ​​sicher. Shellcheck kann sich auch nicht entscheiden. Es ist beides:

"$(dirname $0)"/stop.bash
           ^-- SC2086: Double quote to prevent globbing and word splitting.

Und:

$(dirname "$0")/stop.bash
^-- SC2046: Quote this to prevent word splitting.

Was ist die Logik dahinter?

(Es ist übrigens Shellcheck Version 0.4.4.)

Tomasz
quelle
1
Zitiere alle Substitutionen.
Ignacio Vazquez-Abrams
3
Einfach so "$(dirname "$0")"/stop.bash:? Es scheint zu funktionieren ... Was ist die Geschichte?
Tomasz
1
bash ist schlau genug, um zu wissen, wann sich der Kontext geändert hat.
Ignacio Vazquez-Abrams
1
Stellen shell_level_1 $(shell_level_2) shell_level_1Sie sich das folgendermaßen vor $(....):: Wenn Sie sich in der Shell befinden, geben Sie eine "Unterebene" der Shell ein, ABER Sie können darin schreiben, als ob Sie sich auf der Primärebene befänden (dh Sie können direkt "anstelle von \"usw. schreiben ). Beispiel: touch "/tmp/a file" ; echo "its size is: $(find "/tmp/a file" -ls | awk '{print $5}) ..." : if you used backticks you'd have to Finden Sie "/ tmp / a file" und print \$5. Mit $(...)keine Notwendigkeit: die Schale passt sich an die neue Ebene und Sie können auch direkt schreiben , als ob Ihr Dolmetscher jetzt auf dieser Ebene auch.
Olivier Dulac
"Shellcheck kann sich auch nicht entscheiden." - Die beiden Läufe haben zwei verschiedene Meldungen und zeigen auf verschiedene Stellen. Es will beides.
Izkata

Antworten:

45

Sie müssen verwenden "$(somecmd "$file")".

Ohne die Anführungszeichen wird ein Pfad mit einem Leerzeichen im Argument für geteilt somecmdund auf die falsche Datei abgezielt. Sie brauchen also Zitate auf der Innenseite.

Alle Leerzeichen in der Ausgabe von somecmdverursachen ebenfalls eine Aufteilung. Daher müssen Sie die gesamte Befehlsersetzung in Anführungszeichen setzen.

Anführungszeichen innerhalb der Befehlsersetzung haben keine Auswirkung auf die Anführungszeichen außerhalb der Befehlsersetzung. Bashs eigenes Referenzhandbuch ist dazu nicht allzu klar, aber BashGuide erwähnt es ausdrücklich . Der Text in POSIX erfordert dies auch, da "jedes gültige Shell-Skript" darin zulässig ist $(...):

Mit dem $(command)Formular bilden alle Zeichen, die auf die offene Klammer bis zur passenden schließenden Klammer folgen, den Befehl. Für den Befehl kann ein beliebiges gültiges Shell-Skript verwendet werden, mit Ausnahme eines Skripts, das ausschließlich aus Umleitungen besteht und nicht angegebene Ergebnisse liefert.


Beispiel:

$ file="./space here/foo"

ein. Keine Angebote, dirnameverarbeitet beides ./spaceund here/foo:

$ printf "<%s>\n" $(dirname $file)
<.>
<here>

b. Zitate innerhalb, dirnameProzesse ./space here/foo, Geben ./space here, die in zwei Teile geteilt sind:

$ printf "<%s>\n" $(dirname "$file")
<./space>
<here>

c. Anführungszeichen außerhalb dirnameverarbeiten beide ./spaceund here/fooAusgaben in separaten Zeilen, aber jetzt bilden die beiden Zeilen ein einziges Argument :

$ printf "<%s>\n" "$(dirname $file)"
<.
here>

d. Zitate sowohl innerhalb als auch außerhalb, dies gibt die richtige Antwort:

$ printf "<%s>\n" "$(dirname "$file")"
<./space here>

(Das wäre möglicherweise einfacher gewesen, wenn dirnamenur das erste Argument verarbeitet worden wäre, aber das würde den Unterschied zwischen den Fällen a und c nicht aufzeigen.)

Beachten Sie, dass Sie mit dirname(und möglicherweise mit anderen) auch hinzufügen möchten, um --zu verhindern, dass der Dateiname als Option verwendet wird, falls er mit einem Bindestrich beginnt "$(dirname -- "$file")". Verwenden Sie daher .

ilkkachu
quelle
16
Hinweis: Im Allgemeinen werden Anführungszeichen nicht in Bash verschachtelt. In diesem Fall $( )wird jedoch ein neuer Kontext erstellt, sodass die darin enthaltenen Anführungszeichen unabhängig von den Anführungszeichen außerhalb des Kontexts sind. Und in der Regel möchten Sie in beiden Kontexten Zitate.
Gordon Davisson