Wie teilt man einen String mit Anführungszeichen (wie Befehlsargumente) in bash auf?

6401
foxneSs

Ich habe eine Zeichenfolge wie diese:

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

Ich möchte es so teilen können:

aString that may haveSpaces IN IT bar foo bamboo  bam boo 

Wie mache ich das? (vorzugsweise unter Verwendung eines Einliners)

6
[so] duplicate: [Zeichenfolge nur durch Leerzeichen trennen, die außerhalb von Anführungszeichen stehen] (http://stackoverflow.com/q/12821302) DavidPostill vor 8 Jahren 0
@DavidPostill die Fragen sind eigentlich ganz anders. foxneSs vor 8 Jahren 0
Nicht wirklich, es ist das gleiche allgemeine Problem. DavidPostill vor 8 Jahren 0
@DavidPostill - Dies ist ein viel einfacheres Problem: Alles, was es braucht, ist "für" aString that maySpaces IN IT "bar foo" Bambus "" bam boo "; Echo $ l; fertig " AFH vor 8 Jahren 0
@AFH lol. Ich habe gerade eine viel längere Antwort gepostet. Der einzige Unterschied in der Ausgabe war, dass meine die `` s konservierte. Ich habe die Tatsache übersehen, dass das OP sie in der Ausgabe nicht benötigt. DavidPostill vor 8 Jahren 0
@AFH Sie sollten Ihren Kommentar als Antwort posten. DavidPostill vor 8 Jahren 0
@DavidPostill - Es ist komplizierter, wenn sich der String in einer Variablen befindet. Wenn die Zeichenfolge in `$ s` ist, dann` für l in $ s; Echo $ l; done` nimmt die Anführungszeichen als Literale an und unterbricht die Leerzeichen. Ich muss jetzt raus, also zögern Sie nicht, es herauszufinden. AFH vor 8 Jahren 0
Es heißt Tokenisierung eines Strings. ZB moderne Programmiersprachen / Skriptsprachen / Bibliotheken verfügen über eine String-Tokenizer-Funktion. Für Bash http://stackoverflow.com/questions/5382712/bash-how-to-tokenize-a-string-variable barlop vor 8 Jahren 0
@ barlop Das Token in der verknüpften Frage wird in jeden Bereich aufgeteilt, nicht nur in Anführungszeichen. DavidPostill vor 8 Jahren 0

5 Antworten auf die Frage

3
DavidPostill

Wie mache ich das?

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

Was mache ich, wenn sich meine Zeichenfolge in einer bashVariablen befindet?

Der einfache Ansatz der Verwendung des bashZeichenfolgen-Tokens funktioniert nicht, da er sich in jeden Bereich aufteilt, nicht nur in die außerhalb von Anführungszeichen stehenden:

DavidPostill@Hal /f/test $ cat ./test.sh #! /bin/bash string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' for word in $string; do echo "$word"; done  DavidPostill@Hal /f/test $ ./test.sh "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" 

Um dies zu umgehen, zeigt das folgende Shell-Skript (splitstring.sh) einen Ansatz:

#! /bin/bash  string=$(cat <<'EOF' "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"  EOF ) echo Source String: "$string" results=() result='' inside='' for (( i=0 ; i<${#string} ; i++ )) ; do char=$ if [[ $inside ]] ; then if [[ $char == \\ ]] ; then if [[ $inside=='"' && $ == '"' ]] ; then let i++ char=$inside fi elif [[ $char == $inside ]] ; then inside='' fi else if [[ $char == ["'"'"'] ]] ; then inside=$char elif [[ $char == ' ' ]] ; then char='' results+=("$result") result='' fi fi result+=$char done if [[ $inside ]] ; then echo Error parsing "$result" exit 1 fi  echo "Output strings:" for r in "$" ; do echo "$r" | sed "s/\"//g" done 

Ausgabe:

$ ./splitstring.sh Source String: "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" Output strings: aString that may haveSpaces IN IT bar foo bamboo bam boo 

Quelle: StackOverflow Antwort Trennen Sie einen String nur durch Leerzeichen, die außerhalb von Anführungszeichen von Choroba stehen . Das Skript wurde an die Anforderungen der Frage angepasst.

3
Shogan Aversa-Druesne

Die einfachste Lösung besteht darin, ein Array aus den zitierten Argumenten zu erstellen, das Sie dann übergehen können, wenn Sie möchten, oder direkt an einen Befehl übergeben.

eval "array=($string)"  for arg in "$"; 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 ordnungsgemäß zitierte Version. Hinweis: Dies ist ein Overkill und hinterlässt in doppelten oder einfach zitierten Abschnitten zusätzliche Backslashs, die vor den meisten Interpunktionen stehen, sind aber für den Angriff unverwundbar.

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

Ich überlasse es dem interessierten Leser, ihn zu ändern, wenn er http://ideone.com/FUTHhj passt

2
AFH

Als ich die Antwort von David Postill sah, dachte ich, "es muss eine einfachere Lösung geben". Nach einigem Experimentieren habe ich folgende Arbeiten gefunden:

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 (die Anführungszeichen entfernt und erweitert werden string), bevor die resultierende Zeile (die Inline-Antwort) ausgeführt wird:

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

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

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

Hier stringwird innerhalb der Anführungszeichen erweitert, aber es $muss mit Escapezeichen versehen werden, damit wordin nicht expandiert wird, bevor die Zeile ausgeführt wird (in der anderen Form hat die Verwendung von Anführungszeichen die gleiche Wirkung). 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 
2
Hubbitus

Sie können es declarestatt mit evalzum Beispiel tun :

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 "$"; do echo "[$item]"; done 

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

Wenn Sie es also mit say string versuchen:

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

Du wirst hostnameausgewertet (da kann natürlich sowas sein rm -rf /)!

Ein sehr einfacher Versuch, ihn zu schützen, ersetzt 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 "$"; do echo "[$item]"; done 

Jetzt haben Sie folgende Ausgabe erhalten:

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

Weitere Einzelheiten zu den Methoden und Vor- und Nachteilen finden Sie in dieser guten Antwort: https://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i- Verwenden Sie stattdessen / 17529221 # 17529221

Aber es gab immer noch einen Vektor für einen Angriff. Ich möchte in der Bash-Methode ein String-Anführungszeichen wie in Anführungszeichen (") haben, ohne den Inhalt zu interpretieren .

0
tinyhare

Verwenden Sie awk

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN }' aString that may haveSpaces IN IT bar foo bamboo bam boo 

Oder wandle das Leerzeichen in "% 20" oder "_" um, damit es mit dem nächsten Befehl throw pip bearbeitet werden kann:

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN print }' aString_that_may_haveSpaces_IN_IT bar foo bamboo bam_boo 

Referenz: Awk betrachtet doppelte Anführungszeichen als ein Token und ignoriert Leerzeichen dazwischen