Befehlsausgabe nach Zeilenumbruch teilen?

9

Ich habe einen Befehl, der mehrere Zeilen zurückgibt. Für die weitere Verarbeitung muss ich jede einzelne Zeile dieser Zeilen verarbeiten.

Mein aktueller Code funktioniert durch Ändern des IFS ( Internal Field Separator ):

ROWS=$(some command returning multiple lines)

O=$IFS #save original IFS
IFS=$(echo -en "\n\b") # set IFS to linebreak

for ROW in ${ROWS[@]}
do
  echo "$ROW"
done

IFS=$O #restore old IFS

Ich frage mich, gibt es eine bessere Möglichkeit, auf die einzelnen Zeilen der Ausgabe mehrerer Zeilen zuzugreifen, ohne das IFS zu ändern? Insbesondere die Lesbarkeit meines Skripts wird durch das Ändern des IFS beeinträchtigt.

Update: Ich habe Probleme, die Antworten zum Laufen zu bringen, z. B. die von Choroba:

while IFS= read -r line ; do
    let var+=line #line 42
done << $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
echo "$var" # line 44

gibt mir

./bla.sh: row 44: Warning: here-document at line 43 delimited by end-of-file (wanted `$(sqlite3 -list -nullvalue NULL -separator , /var/log/asterisk/master.db ${QUERY})')
./bla.sh: row 42: let: echo "": syntax error: invalid arithmetic operator. (error causing character is \"""\").

Kann mir jemand dabei helfen? Vielen Dank!

stefan.at.wpf
quelle
chorobas antwort sagt zu tun < <(some command returning multiple lines), aber das ist nicht was du tust.
Mikel

Antworten:

13

Was ist mit readeiner whileSchleife?

some command returning multiple lines | while IFS= read -r line ; do
    echo "$line"
done

Beachten Sie jedoch, dass die Pipe in einer Subshell ausgeführt wird. Dies bedeutet, dass Sie die Variablenwerte für den Rest des Skripts nicht ändern können. Wenn Sie etwas benötigen, um von Ihrer while-Schleife zu überleben, können Sie die Prozessersetzung von bash verwenden:

while IFS= read -r line ; do
    let var+=line
done < <(some command returning multiple lines)
echo "$var"
Choroba
quelle
Die Verwendung der Prozesssubstitution ist eine clevere Lösung für dieses bashProblem. Es ist jedoch anzumerken, dass das Subshell-Scoping von Variablen nur ein Problem darstellt. Wenn Sie bashdasselbe tun, zshwürde dieses Problem überhaupt nicht auftreten.
Caleb
ksh hat dieses Problem auch nicht.
Didi Kohen
Vielen Dank, ich habe diese Lösung ausprobiert, aber ich kann sie nicht zum Laufen bringen. Bitte lesen Sie meinen aktualisierten Originalbeitrag. Danke!
stefan.at.wpf
@ stefan.at.wpf: Sie können keine Leerzeichen und Dollarzeichen nach Belieben platzieren. Sie haben Bedeutung.
Choroba
3

Es IFSreicht nicht aus, nur eine neue Zeile festzulegen. (Warum teilen Sie übrigens auch Rücktaste auf?)

In Ihrem Code ${ROWS[@]}(was eine seltsame Art zu schreiben ist $ROWS- ROWSist kein Array) wird nicht in doppelte Anführungszeichen gesetzt. (Wenn es sich in doppelten Anführungszeichen befindet, erhalten Sie eine einzelne Zeichenfolge, da ROWSes sich nicht um ein Array handelt.) Die Shell teilt den Wert der Variablen bei jedem IFSZeichen in Felder auf und behandelt dann jedes Feld als Glob-Muster. Wenn beispielsweise eine der vom Befehl gedruckten Zeilen das einzelne Zeichen enthält *, wird dieses durch die Dateinamen im aktuellen Verzeichnis ersetzt.

Sie können das Globbing mit deaktivieren set -f. In den meisten Fällen, in denen Sie die IFSFeldaufteilungsfunktion der Shell verwenden möchten, müssen Sie auch das Globbing deaktivieren. Setzen Sie es wieder mit set +f.

Die zuverlässige Redewendung zum zeilenweisen Lesen der Ausgabe eines Befehls lautet while IFS= read -r.

some command returning multiple lines |
while IFS= read -r ROW; do
  
done

Beachten Sie, dass die meisten Shells jeden Befehl einer Pipeline in einer separaten Subshell ausführen. Wenn Sie also Variablen festlegen und nach der Schleife verwenden müssen, schließen Sie diese Befehle zusammen mit der Schleife in eine Gruppe ein. (Ksh und zsh sind die Ausnahmen, sie führen den letzten Befehl einer Pipeline in der übergeordneten Shell aus.)

some command returning multiple lines | {
  while IFS= read -r ROW; do
    
    row_count=$((row_count+1))
  done
  echo "There were $row_count rows."
}
Gilles 'SO - hör auf böse zu sein'
quelle
1

Sie mischen die Here-Document- und Here-String-Syntax in Ihrer Update-Frage.

Verwenden Sie entweder hier-Dokument:

while IFS= read -r line ; do
    let var+=line #line 42
done <<ENDMARK
$(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
ENDMARK

Oder hier-String:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
Mann bei der Arbeit
quelle