grep file dari daftar

14

Saya mencoba menjalankan grep terhadap daftar beberapa ratus file:

$ head -n 3 <(cat files.txt)
admin.php
ajax/accept.php
ajax/add_note.php

Namun, meskipun saya mencari string yang saya tahu ditemukan dalam file, berikut ini tidak mencari file:

$ grep -i 'foo' <(cat files.txt)

$ grep -i 'foo' admin.php
The foo was found

Saya kenal dengan -fflag yang akan membaca pola dari file. Tapi bagaimana cara membaca file input ?

Saya telah mempertimbangkan solusi yang mengerikan dari menyalin file ke direktori sementara karena cptampaknya mendukung <(cat files.txt)format, dan dari sana mengambil file. Shirley ada cara yang lebih baik.

dotancohen
sumber

Jawaban:

22

Anda tampaknya memahami daftar nama file, bukan file itu sendiri. <(cat files.txt)hanya daftar file. Cobalah <(cat $(cat files.txt))menyatukan mereka dan mencari mereka sebagai satu aliran, atau

grep -i 'foo' $(cat files.txt)

untuk memberikan grep semua file.

Namun, jika ada terlalu banyak file dalam daftar, Anda mungkin memiliki masalah dengan jumlah argumen. Dalam hal ini saya baru saja menulis

while read filename; do grep -Hi 'foo' "$filename"; done < files.txt
orion
sumber
Terima kasih! Saya tidak menyadari bahwa whilebisa menerima baris file.txt seperti itu.
dotancohen
Anda akan ingin menonaktifkan bagian glob dari operator glob + split di sini (kecuali shellnya adalah zsh).
Stéphane Chazelas
1
whiletidak persis menerima baris dari file, readmelakukan itu; whilebiarkan kami melakukannya dalam satu lingkaran. Loop berakhir ketika readgagal (yaitu mengembalikan kode pengembalian non-nol), biasanya karena Akhir File tercapai.
PM 2Ring
1
Untuk membaca baris (teks), sintaksnya IFS= read -r filename, read filenameadalah sesuatu yang lain.
Stéphane Chazelas
1
Perhatikan bahwa itu -Hadalah ekstensi GNU. Anda melewatkan beberapa --.
Stéphane Chazelas
8
xargs grep -i -- foo /dev/null < files.txt

dengan asumsi file kosong atau baris baru dibatasi (di mana tanda kutip atau garis miring terbalik dapat digunakan untuk menghindari pemisah tersebut). Dengan GNU xargsAnda dapat menentukan pembatas dengan -d(yang kemudian menonaktifkan penanganan kutipan).

(unset -v IFS; set -f; grep -i -- foo $(cat files.txt))

dengan asumsi file terpisah ruang, tab atau baris baru (tidak ada cara untuk menghindarinya meskipun Anda dapat memilih pemisah yang berbeda dengan menugaskannya IFS). Yang itu akan gagal jika daftar file terlalu besar pada kebanyakan sistem.

Mereka juga berasumsi bahwa tidak ada file yang dipanggil -.

Stéphane Chazelas
sumber
Lebih baik / lebih cepat untuk digunakan $(< file)daripada $(cat file), setidaknya dalam bashdan zsh.
jimmij
7

Untuk membaca daftar nama file dari stdin dapat Anda gunakan xargs. Misalnya,

cat files.txt | xargs -d'\n' grep -i -- 'foo'

Secara default, xargsmembaca item dari input standar, dibatasi oleh kosong. The -d'\n'mengatakan itu untuk menggunakan baris baru sebagai pembatas argumen, sehingga dapat menangani nama file yang berisi kosong. (Seperti yang ditunjukkan Stéphane Chazelas, itu adalah ekstensi GNU). Namun, itu tidak akan mengatasi nama file yang mengandung baris baru; kita akan membutuhkan pendekatan yang sedikit lebih rumit untuk mengatasinya.

FWIW, pendekatan ini agak lebih cepat daripada while readloop, karena readperintah bash sangat lambat - ia membaca karakter data-nya dengan karakter, sedangkan xargsmembaca inputnya lebih efisien. Juga, xargshanya menjalankan grepperintah sebanyak yang diperlukan, dengan setiap doa menerima beberapa nama file, dan itu lebih efisien daripada memanggil secara grepindividual untuk setiap nama file.

Lihat halaman manual xargs dan halaman info xargs untuk perincian lebih lanjut.

PM 2Ring
sumber
3

xargsdapat membaca item dari file (seperti files.txtdaftar Anda ) dengan opsi itu:

   --arg-file=file
   -a file
          Read items from file instead of standard input.  If you use this
          option, stdin remains unchanged when commands are  run.   Other
          wise, stdin is redirected from /dev/null.

Jadi ini harus bekerja juga:

xargs -a files.txt grep -i 'foo'

atau untuk spasi dalam nama file

xargs -d'\n' -a files.txt grep -i 'foo'
xargs -I{} -a files.txt grep -i 'foo' {}
Xen2050
sumber
1

Anda juga dapat melakukan for for tetapi contoh Orion adalah yang paling sederhana:

for i in $(cat files.txt); do grep -i 'foo' $i ; done

(Untuk setiap file yang terdaftar di files.txt, jalankan perintah grep di atasnya.)

Michael
sumber