Membagi output perintah dengan linebreak?


9

Saya punya perintah mengembalikan beberapa baris. Untuk pemrosesan lebih lanjut, saya perlu memproses setiap baris dari baris tersebut.

Kode saya saat ini berfungsi dengan memodifikasi IFS ( Pemisah Bidang Internal ):

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

Saya bertanya-tanya, apakah ada cara yang lebih baik untuk mengakses satu baris dari beberapa garis keluaran, satu tanpa memodifikasi IFS? Terutama keterbacaan skrip saya menjadi buruk dengan memodifikasi IFS.

Pembaruan: Saya mengalami masalah untuk mendapatkan jawaban berfungsi, mis. Yang dari 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

memberi saya

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

Adakah yang bisa membantu saya dengan ini? Terima kasih!


Jawaban choroba mengatakan harus dilakukan < <(some command returning multiple lines), tetapi bukan itu yang Anda lakukan.
Mikel

Jawaban:


13

Bagaimana dengan readdalam satu whilelingkaran?

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

Hati-hati, meskipun, pipa dijalankan dalam subkulit, yang berarti Anda tidak dapat mengubah nilai variabel untuk sisa skrip. Jika Anda membutuhkan sesuatu untuk bertahan dari loop sementara Anda, Anda dapat menggunakan subtitusi proses bash:

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

Menggunakan subtitusi proses adalah solusi cerdas untuk ini bash, tetapi perlu dicatat bahwa cakupan subkulit variabel hanya masalah bash, melakukan hal yang sama zshtidak akan memiliki masalah ini sama sekali.
Caleb

ksh juga tidak memiliki masalah ini.
Didi Kohen

terima kasih kawan, saya mencoba solusi ini, tetapi saya tidak bisa membuatnya bekerja, silakan lihat posting asli saya yang diperbarui, terima kasih!
stefan.at.wpf

@ stefan.at.wpf: Anda tidak dapat menempatkan spasi dan tanda dolar sesuka hati. Mereka memiliki makna.
choroba

3

Pengaturan IFSke baris baru saja tidak cukup. (Ngomong-ngomong, kamu juga membelah karakter backspace?)

Dalam kode Anda, ${ROWS[@]}(yang merupakan cara aneh untuk menulis $ROWS- ROWSbukan array) tidak dikutip ganda. (Jika berada di dalam tanda kutip ganda, Anda akan mendapatkan string tunggal, karena ROWSbukan array.) Jadi shell membagi nilai variabel menjadi bidang pada setiap IFSkarakter, lalu memperlakukan setiap bidang sebagai pola gumpalan. Misalnya, jika salah satu baris yang dicetak oleh perintah berisi karakter tunggal *, ini akan diganti dengan nama file di direktori saat ini.

Anda dapat mematikan globbing dengan set -f. Dalam kebanyakan kasus di mana Anda mengatur IFSuntuk menggunakan fitur pemecahan bidang shell, Anda juga perlu mematikan globbing. Nyalakan kembali set +f.

Ungkapan yang dapat diandalkan untuk membaca baris output perintah demi baris adalah while IFS= read -r.

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

Perhatikan bahwa sebagian besar shell menjalankan setiap perintah pipa dalam subkulit yang terpisah. Jadi jika Anda perlu mengatur variabel dan menggunakannya setelah loop, bungkus perintah ini dalam grup bersama dengan loop. (Ksh dan zsh adalah pengecualian, mereka menjalankan perintah terakhir dari pipeline di shell induk.)

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

1

Anda sedang mencampur sintaks di sini-dokumen dan di sini-string dalam pertanyaan pembaruan Anda.

Baik gunakan di sini-dokumen:

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

Atau di sini-string:

while IFS= read -r line ; do
    let var+=line #line 42
done <<< $(sqlite3 -list -nullvalue NULL -separator ',' /var/log/asterisk/master.db "${QUERY}")
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.