Dividi l'output del comando per interruzione di riga?


9

Ho un comando che restituisce più righe. Per ulteriori elaborazioni ho bisogno di elaborare ogni singola riga di quelle righe.

Il mio codice attuale funziona modificando l'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

Mi chiedo, c'è un modo migliore per accedere alle singole linee dell'uscita su più linee, una senza modificare l'IFS? Soprattutto la leggibilità della mia sceneggiatura peggiora modificando l'IFS.

Aggiornamento: ho problemi a far funzionare le risposte, ad esempio quello di 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

mi da

./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 \"""\").

Qualcuno può aiutarmi con questo? Grazie!


La risposta di Choroba dice di fare < <(some command returning multiple lines), ma non è quello che stai facendo.
Mikel,

Risposte:


13

Che dire readdi un whileciclo?

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

Fai attenzione, tuttavia, la pipe viene eseguita in una subshell, il che significa che non puoi modificare i valori delle variabili per il resto dello script. Se hai bisogno di qualcosa per sopravvivere dal tuo ciclo while, puoi usare la sostituzione del processo di bash:

while IFS= read -r line ; do
    let var+=line
done < <(some command returning multiple lines)
echo "$var"

L'uso della sostituzione di processo è una soluzione intelligente per questo in bash, ma vale la pena notare che lo scoping delle variabili della subshell è solo un problema bash, fare la stessa cosa zshnon avrebbe affatto questo problema.
Caleb,

ksh non ha neanche questo problema.
Didi Kohen,

grazie ragazzi, ho provato questa soluzione, ma non riesco a farlo funzionare, vedere il mio post originale aggiornato, grazie!
stefan.at.wpf,

@ stefan.at.wpf: non puoi posizionare spazi e segni di dollaro a piacimento. Hanno un significato.
Choroba,

3

L'impostazione IFSsu una sola riga non è sufficiente. (Perché stai anche dividendo in caratteri backspace, comunque?)

Nel tuo codice ${ROWS[@]}(che è uno strano modo di scrivere $ROWS- ROWSnon è un array) non è racchiuso tra virgolette. (Se fosse racchiuso tra virgolette doppie, otterresti una singola stringa, poiché ROWSnon è un array.) Quindi la shell divide il valore della variabile in campi in ciascun IFScarattere, quindi tratta ogni campo come un modello glob. Ad esempio, se una delle righe stampate dal comando contiene il singolo carattere *, questo verrà sostituito dai nomi dei file nella directory corrente.

Puoi disattivare il globbing con set -f. Nella maggior parte dei casi in cui si imposta IFSl'utilizzo della funzione di suddivisione del campo della shell, è inoltre necessario disattivare il globbing. Ripartilo con set +f.

Il linguaggio affidabile per leggere l'output di un comando riga per riga è while IFS= read -r.

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

Si noti che la maggior parte delle shell esegue ogni comando di una pipeline in una sottoshell separata. Quindi, se è necessario impostare le variabili e usarle dopo il ciclo, avvolgere questi comandi in un gruppo insieme al ciclo. (Ksh e zsh sono le eccezioni, eseguono l'ultimo comando di una pipeline nella shell padre.)

some command returning multiple lines | {
  while IFS= read -r ROW; do
    
    row_count=$((row_count+1))
  done
  echo "There were $row_count rows."
}

1

Stai mescolando la sintassi here-document e here-string nella tua domanda di aggiornamento.

O utilizzare qui-document:

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

O qui-stringa:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.