Wie teilt man in Bash eine Zeichenfolge mit Anführungszeichen (wie Befehlsargumente)?

Wie teilt man in Bash eine Zeichenfolge mit Anführungszeichen (wie Befehlsargumente)?

Ich habe eine Zeichenfolge wie diese:

"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"

Ich möchte es folgendermaßen aufteilen können:

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

Wie mache ich das? (am besten mit einem Einzeiler)

Antwort1

Als ich die Antwort von David Postill sah, dachte ich: „Es muss eine einfachere Lösung geben.“ Nach einigem Experimentieren fand ich Folgendes heraus:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo $string
eval 'for word in '$string'; do echo $word; done'

Dies funktioniert, weil evaldie Zeile erweitert wird (indem die Anführungszeichen entfernt und erweitert werden string), bevor die resultierende Zeile (die die Inline-Antwort ist) ausgeführt wird:

for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done

Eine Alternative, die sich auf dieselbe Zeile erweitert, ist:

eval "for word in $string; do echo \$word; done"

Hier stringwird innerhalb der Anführungszeichen erweitert, aber es $muss maskiert werden, damit wordes nicht erweitert wird, bevor die Zeile ausgeführt wird (in der anderen Form hat die Verwendung von einfachen Anführungszeichen den gleichen Effekt). Die Ergebnisse sind:

[~/]$ string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
[~/]$ echo $string
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
[~/]$ eval 'for word in '$string'; do echo $word; done'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
[~/]$ eval "for word in $string; do echo \$word; done"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

Antwort2

Die einfachste Lösung besteht darin, ein Array der zitierten Argumente zu erstellen, über das Sie dann bei Bedarf eine Schleife ausführen oder das Sie direkt an einen Befehl übergeben können.

eval "array=($string)"

for arg in "${array[@]}"; do echo "$arg"; done   

PS: Bitte kommentieren Sie, wenn Sie einen einfacheren Weg ohne finden eval.

Bearbeiten:

Aufbauend auf der Antwort von @Hubbitus haben wir eine vollständig bereinigte und korrekt zitierte Version. Hinweis: Dies ist übertrieben und hinterlässt tatsächlich zusätzliche Backslashes in doppelten oder einfachen Anführungszeichen vor den meisten Satzzeichen, ist aber gegen Angriffe immun.

declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))"

Ich überlasse es dem interessierten Leser, nach eigenem Ermessen Änderungen vorzunehmen.http://ideone.com/FUTHhj

Antwort3

Es sieht so aus, als ob xargs das ziemlich gut kann:

$ a='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
$ printf "%s" "$a" | xargs -n 1 printf "%s\n"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

Antwort4

Sie können es mit declareanstelle von tun eval, zum Beispiel:

Anstatt:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo "Initial string: $string"
eval 'for word in '$string'; do echo $word; done'

Tun:

declare -a "array=($string)"
for item in "${array[@]}"; do echo "[$item]"; done

Beachten Sie jedoch, dass es nicht viel sicherer ist, wenn die Eingabe vom Benutzer kommt!

Versuchen Sie es beispielsweise mit einer Zeichenfolge wie der folgenden:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'

Du wirst hostnamebewertet (da kann es natürlich auch so etwas geben rm -rf /)!

Ein sehr, sehr einfacher Versuch, es zu schützen, ersetzen Sie einfach Zeichen wie Backtrick ` und $:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
declare -a "array=( $(echo $string | tr '`$<>' '????') )"
for item in "${array[@]}"; do echo "[$item]"; done

Jetzt erhalten Sie eine Ausgabe wie:

[aString that may haveSpaces IN IT]
[bar]
[foo]
[bamboo]
[bam boo]
[?hostname?]

Weitere Einzelheiten zu Methoden sowie Vor- und Nachteilen finden Sie möglicherweise in dieser guten Antwort:https://stackoverflow.com/questions/17529220/warum-sollte-eval-in-bash-vermieden-werden-und-was-sollte-ich-stattdessen-verwenden/17529221#17529221

Es blieb jedoch noch immer eine Angriffsmöglichkeit. Ich möchte in der Bash-Methode unbedingt Anführungszeichen wie in doppelten Anführungszeichen (") haben, aber ohne den Inhalt zu interpretieren.

verwandte Informationen