Durchlaufen Sie eine durch Kommas getrennte Shell-Variable

107

Angenommen, ich habe eine Unix-Shell-Variable wie unten

variable=abc,def,ghij

Ich möchte , um alle Werte extrahieren ( abc, defund ghij) einen for - Schleife und jeden Wert in eine Prozedur übergeben.

Das Skript sollte das Extrahieren einer beliebigen Anzahl von durch Kommas getrennten Werten ermöglichen $variable.

Ramanathan K.
quelle
Was genau möchten Sie tun, indem Sie es wiederholen?
SMA
1
Ich möchte jedes Feld (sagen wir abc) als Argument an eine Prozedur übergeben.
Ramanathan K

Antworten:

123

Mit dem folgenden Skript können Sie Ihre Variable dynamisch durchlaufen, unabhängig davon, wie viele Felder sie enthält, solange sie nur durch Kommas getrennt ist.

variable=abc,def,ghij
for i in $(echo $variable | sed "s/,/ /g")
do
    # call your procedure/other scripts here below
    echo "$i"
done

Anstelle des echo "$i"obigen Aufrufs zwischen dound doneinnerhalb der for-Schleife können Sie Ihre Prozedur aufrufen proc "$i".


Update : Das obige Snippet funktioniert, wenn der Wert der Variablen keine Leerzeichen enthält. Wenn Sie eine solche Anforderung haben, verwenden Sie bitte eine der Lösungen, die sich ändern können, IFSund analysieren Sie dann Ihre Variable.


Hoffe das hilft.

Anakron
quelle
8
Dies funktioniert nicht, wenn $variableLeerzeichen enthalten sind, z. B. variable=a b,c,d=> druckt 4 Zeilen (a | b | c | d) anstatt 3 (ab | c | d)
Dan
Außerdem werden neue Anführungszeichen wie ** "value ** hinzugefügt .
Könnten
136

Nicht mit IFS herumspielen
Kein externer Befehl aufrufen

variable=abc,def,ghij
for i in ${variable//,/ }
do
    # call your procedure/other scripts here below
    echo "$i"
done

Verwenden der Bash-String-Manipulation http://www.tldp.org/LDP/abs/html/string-manipulation.html

neric
quelle
3
Das Schöne daran ist, dass Sie mehrere Schleifen mit unterschiedlichen Trennzeichen verschachteln können. Guter Vorschlag!
Brad Parks
5
Netter Tipp, um Unordnung zu vermeiden IFS!
Laimoncijus
13
Kleine Warnung - Wenn einer der Werte in $iLeerzeichen enthält, werden diese aufgeteilt (und dies könnte der Grund dafür sein, dass er eher durch Kommas als durch Leerzeichen getrennt übergeben wird)
Toby Speight,
4
Wenn eine vorherige Funktion die IFS-Einstellung geändert hat, funktioniert dies nicht ... Ändern Sie dies in:for i in ${variable//,/$IFS} do; echo "$i"; done
Joep
2
Ich erhalte die Fehlermeldung "Bad Substitution", wenn ich diesen Ansatz versuche.
Lost Crotchet
55

Wenn Sie ein anderes Feldtrennzeichen festlegen, können Sie direkt eine forSchleife verwenden:

IFS=","
for v in $variable
do
   # things with "$v" ...
done

Sie können die Werte auch in einem Array speichern und dann wie in Wie teile ich eine Zeichenfolge in einem Trennzeichen in Bash? ::

IFS=, read -ra values <<< "$variable"
for v in "${values[@]}"
do
   # things with "$v"
done

Prüfung

$ variable="abc,def,ghij"
$ IFS=","
$ for v in $variable
> do
> echo "var is $v"
> done
var is abc
var is def
var is ghij

In dieser Lösung finden Sie einen breiteren Ansatz zum Durchlaufen einer durch Kommas getrennten Liste und Ausführen eines Befehls für jeden Eintrag .

Beispiele für den zweiten Ansatz:

$ IFS=, read -ra vals <<< "abc,def,ghij"
$ printf "%s\n" "${vals[@]}"
abc
def
ghij
$ for v in "${vals[@]}"; do echo "$v --"; done
abc --
def --
ghij --
fedorqui 'SO hör auf zu schaden'
quelle
Siehe auch stackoverflow.com/questions/918886/… :-) Viel Glück an alle.
Shellter
Ja! Ich habe auch darüber nachgedacht, nur dass es dann Schritte erfordert: a whilezum Einlesen in ein Array und ein anderes zum Durchlaufen der Ergebnisse.
fedorqui 'SO hör auf,'
3
Es freut mich, wenn jemand tatsächlich weiß, wie man die Schale benutzt, anstatt ein paar Binutils zusammenzupfeifen.
Alanwaring
Müssen Sie sich auf das IFSzurücksetzen, was es vorher war?
Pstanton
1
@ Fedorqui Es hat! Dies ist die einzige Variable, die ich durchlaufen wollte, und sie erleichterte das Lesen meiner Yaml-Datei (anstelle einer langen Umgebungsvariablen enthält sie eine Reihe von Zeilen)
GammaGames
5
#/bin/bash   
TESTSTR="abc,def,ghij"

for i in $(echo $TESTSTR | tr ',' '\n')
do
echo $i
done

Ich bevorzuge die Verwendung von tr anstelle von sed, da sed in einigen Fällen Probleme mit speziellen Zeichen wie \ r \ n hat.

Eine andere Lösung besteht darin, IFS auf ein bestimmtes Trennzeichen zu setzen

Marek Lisiecki
quelle
1

Hier ist eine alternative tr-basierte Lösung, die kein Echo verwendet, ausgedrückt als Einzeiler.

for v in $(tr ',' '\n' <<< "$var") ; do something_with "$v" ; done

Es fühlt sich ohne Echo aufgeräumter an, aber das ist nur meine persönliche Präferenz.

6EQUJ5
quelle
0

Probier diese.

#/bin/bash   
testpid="abc,def,ghij" 
count=`echo $testpid | grep -o ',' | wc -l` # this is not a good way
count=`expr $count + 1` 
while [ $count -gt 0 ]  ; do
     echo $testpid | cut -d ',' -f $i
     count=`expr $count - 1 `
done
Karthikeyan.RS
quelle
aber was ist, wenn ich mir über die Anzahl der Felder in der Variablen testpid nicht sicher bin?
Ramanathan K
Außerdem muss ich jedes Feld der obigen Variablen als Argument an eine Prozedur übergeben. Und ich möchte dies tun, bis die Länge der Variablen Null wird. (dh alle Felder sollten einmal an das Verfahren zur Verarbeitung übergeben werden)
Ramanathan K
Ich weiß nichts über das Verfahren. Über diesen Link können Sie die Anzahl der Felder abrufen. http://stackoverflow.com/questions/8629330/unix-count-of-columns-in-file. Danach Machen Sie das für Schleife in while-Schleife. Sie können das bekommen.
Karthikeyan.RS
Dies ist, um die Felder aus einer Datei zu zählen, denke ich. Was ist, wenn ich die Felder in einer Variablen zählen muss (angenommen, die Variable ist testpid = abc, def, ghij)
Ramanathan K
Echo der Variablen und Pipe die Ausgabe an die awk. Oder sehen Sie meine aktualisierte Antwort. Vielleicht ist es nützlich.
Karthikeyan.RS
0

Eine andere Lösung, bei der IFS nicht verwendet wird und die Leerzeichen dennoch erhalten bleiben:

$ var="a bc,def,ghij"
$ while read line; do echo line="$line"; done < <(echo "$var" | tr ',' '\n')
line=a bc
line=def
line=ghij
user3071170
quelle
Dies funktioniert korrekt in Bash, jedoch verschluckt zsh die Leerzeichen.
Lleaff
0

Hier ist meine reine Bash-Lösung, die IFS nicht ändert und einen benutzerdefinierten Regex-Begrenzer verwenden kann.

loop_custom_delimited() {
    local list=$1
    local delimiter=$2
    local item
    if [[ $delimiter != ' ' ]]; then
        list=$(echo $list | sed 's/ /'`echo -e "\010"`'/g' | sed -E "s/$delimiter/ /g")
    fi
    for item in $list; do
        item=$(echo $item | sed 's/'`echo -e "\010"`'/ /g')
        echo "$item"
    done
}
Placidwolverin
quelle