Cara memproses setiap baris yang diterima sebagai hasil dari perintah grep

152

Saya memiliki sejumlah baris yang diambil dari file setelah menjalankan perintah grep sebagai berikut:

var=`grep xyz abc.txt`

Katakanlah saya mendapat 10 baris yang terdiri dari xyz sebagai hasilnya.

Sekarang saya perlu memproses setiap baris yang saya dapatkan sebagai hasil dari perintah grep. Bagaimana saya melanjutkan ini?

XYZ_Linux
sumber
6
Tidak ada jawaban di sini yang menyebutkan kekuatan grep -ountuk hal semacam ini. The -obendera akan memberikan kembali hanya teks yang cocok, dengan satu pertandingan per baris dari output. (Ini tidak lengkap, jadi echo aaa |grep 'a*'hanya memberi Anda "aaa" dan menghilangkan tiga pertandingan parsial "", "a", dan "aa")
Adam Katz

Jawaban:

269

Salah satu cara mudah adalah tidak menyimpan output dalam suatu variabel, tetapi langsung iterate dengan loop while / read.

Sesuatu seperti:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

Ada variasi pada skema ini tergantung pada apa yang Anda cari.

Jika Anda perlu mengubah variabel di dalam loop (dan agar perubahan itu terlihat di luar itu), Anda dapat menggunakan substitusi proses seperti yang dinyatakan dalam jawaban fedorqui :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)
Tikar
sumber
jika tidak ada garis dengan xyz?
XYZ_Linux
Maka tidak ada yang terjadi, loop tidak berjalan.
Mat
13
Masalah dengan metode ini adalah bahwa (karena pipa) semua yang ada di dalam loop berada dalam subkulit, jadi variabel pengaturan yang ditentukan di luar loop selama loop tidak membuat nilainya tersedia setelah loop!
David Doria
3
@ David: memberikan alternatif untuk mengatasi masalah Anda. (fedorqui sudah membahasnya juga.)
Mat
Untuk perintah yang garis keluaran terakhirnya tidak diakhiri dengan baris baru, Anda memerlukan: while read p || [[ -n $p ]]; do ...(dipinjam dari stackoverflow.com/questions/1521462/… )
Ohad Schneider
21

Anda dapat melakukan while readloop berikut , yang akan diumpankan oleh hasil dari grepperintah menggunakan proses yang disebut substitusi:

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

Dengan cara ini, Anda tidak harus menyimpan hasilnya dalam sebuah variabel, tetapi langsung "menyuntikkan" hasilnya ke loop.


Perhatikan penggunaan IFS=dan read -rsesuai dengan rekomendasi di BashFAQ / 001: Bagaimana saya bisa membaca file (aliran data, variabel) baris demi baris (dan / atau bidang-demi-bidang)? :

Opsi -r untuk membaca mencegah interpretasi backslash (biasanya digunakan sebagai pasangan garis miring backslash, untuk melanjutkan beberapa baris atau untuk menghindari pembatas). Tanpa opsi ini, garis miring terbalik yang tidak terhapus di input akan dibuang. Anda hampir selalu harus menggunakan opsi -r dengan membaca.

Dalam skenario di atas IFS = mencegah pemangkasan spasi putih depan dan akhir. Hapus jika Anda menginginkan efek ini.

Mengenai penggantian proses, dijelaskan di halaman peretas bash :

Substitusi proses adalah bentuk pengalihan di mana input atau output dari suatu proses (beberapa urutan perintah) muncul sebagai file sementara.

fedorqui 'SO berhenti merugikan'
sumber
OK, saya menyerang forversinya. Mencoba untuk melakukan loop "${$(grep xyz abc.txt)[@]}"seperti pada stackoverflow.com/a/14588210/1983854 tetapi tidak bisa. Jadi saya hanya meninggalkan versi pertama.
fedorqui 'SO berhenti merugikan'
1
Anda tidak bisa menerapkan ekspansi parameter ke substitusi perintah (kecuali jika Anda menggunakan zsh, di mana sarang semacam itu mungkin bekerja).
chepner
Satu kemungkinan masalah dengan idiom ini adalah bahwa jika ada sesuatu di dalam loop mencoba membaca dari input standar, itu akan mendapatkan bagian dari file. Untuk menghindari kemungkinan ini, saya lebih suka mengirim file melalui file deskriptor 3 daripada stdin. Cukup gunakan while IFS= read -r result <&3dandone 3< <(grep ...
Gordon Davisson
8

Saya akan menyarankan menggunakan awk daripada grep + sesuatu yang lain di sini.

awk '$0~/xyz/{ //your code goes here}' abc.txt

Julien Grenier
sumber
1
Bagaimana Anda mereferensikan baris yang ditemukan lengkap di //your code goes here?
user857990
2
@ user857990 Seluruh baris direpresentasikan sebagai $ 0 dalam AWK.
Julien Grenier
2

Tanpa iterasi dengan opsi grep --line-buffered:

your_command | grep --line-buffered "your search"

Contoh kehidupan nyata dengan perintah debug router Symfony PHP Framework ouput, untuk menangkap semua rute terkait "api":

php bin/console d:r | grep --line-buffered "api"
Nicolas Bonnici
sumber
Satu-satunya solusi yang berfungsi jika perintah_Anda berjalan lama (seperti tail -f some.log, dalam kasus saya) ...
Izkata
0

Seringkali urutan pemrosesan tidak masalah. GNU Parallel dibuat untuk situasi ini:

grep xyz abc.txt | parallel echo do stuff to {}

Jika Anda memproses lebih seperti:

grep xyz abc.txt | myprogram_reading_from_stdin

dan myprogramlambat maka Anda dapat menjalankan:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin
Ole Tange
sumber
0

Iterate atas hasil grep dengan loop while / read. Suka:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done
Abhi km
sumber
0

Bagi mereka yang mencari one-liner:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
laurent
sumber