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?


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)

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.


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


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"

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

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

0

Bagi mereka yang mencari one-liner:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
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.