Bagaimana cara saya melakukan xargs grep pada keluaran grep yang memiliki spasi?

8

Saya mencari file berdasarkan ekspresi reguler, dan kemudian saya mencoba mencari file tersebut untuk konten. Jadi, misalnya, saya punya sesuatu seperti

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp" | grep "<name regex>" | xargs grep "<content regex>"

Masalah yang saya hadapi adalah bahwa beberapa jalur memiliki ruang di dalamnya, yang membingungkan xargs. Saya tahu bahwa jika saya hanya menggunakan find, saya bisa menggunakan -print0argumen (bersama dengan -0argumen pada xargs) untuk menjaga xargs dari memperlakukan ruang sebagai pembatas. Apakah ada yang serupa grep?

Atau apakah saya mendekati masalah ini dengan cara yang salah sepenuhnya? Naif, finduntuk grepuntuk xargs grepmasuk akal bagi saya, tapi aku terbuka untuk pendekatan lain yang menghasilkan hasil yang sama.

quanticle
sumber
2
Anda dapat memposisikan argumen dengan xargsmenggunakan -iparameter, ala cat sample.txt | grep "pat t ern" | xargs -i grep "{}"- kurung kurawal memberi tahu di mana posisi argumen. Manual mengatakan kepada saya bahwa -isudah usang dalam mendukung -Ijadi mungkin perlu melihat itu juga.
dougBTV

Jawaban:

5

Gunakan sesuatu seperti ini mungkin (jika gnu grep).

grep -r 'content pattern' --include==*.cpp

bung

--include = GLOB Cari hanya file yang nama dasarnya cocok dengan GLOB (menggunakan pencocokan wildcard seperti dijelaskan di bawah --exclude)

Juga lihat opsi untuk pembatas nol.

-Z, --null Menghasilkan byte nol (karakter ASCII NUL) alih-alih karakter yang biasanya mengikuti nama file. Sebagai contoh, grep -lZ menghasilkan byte nol setelah setiap nama file, bukan baris baru yang biasa. Pilihan ini membuat output tidak ambigu, bahkan di hadapan nama file yang mengandung karakter yang tidak biasa seperti baris baru. Opsi ini dapat digunakan dengan perintah seperti find -print0, perl -0, sort -z, dan xargs -0 untuk memproses nama file yang sewenang-wenang, bahkan yang berisi karakter baris baru.

-z, --null-data Perlakukan input sebagai satu set garis, masing-masing diakhiri dengan byte nol (karakter ASCII NUL) alih-alih baris baru. Seperti opsi -Z atau --null, opsi ini dapat digunakan dengan perintah seperti sort -z untuk memproses nama file yang berubah-ubah.

Sakit kepala
sumber
Perhatikan bahwa grep -r include='*.cpp'ini adalah shell glob - dan begitu juga fitur-aligned w / find . -name '*.cpp' -exec grep -e 'content_pattern' -- {} \;not w /find . -name '*.cpp' | grep 'name_pattern' | xargs grep 'content_pattern'
mikeserv
4

Jika Anda harus melompati banyak rintangan, maka efisiensi xargs akan hilang. Ini adalah satu pekerjaan kasar:

find . -iname "*.cpp" | grep "<pattern>" | while read -r x; do grep exa "$x"; done

Setiap kali saya mengalami masalah dengan spasi dalam nama file, jawabannya adalah tanda kutip ganda pada variabel.

Baazigar
sumber
Ini menjalankan grep dalam loop secara unik untuk setiap baris yang ditemukan oleh grep luar. Itu banyak overhead.
Adam Katz
3

Gunakan finduntuk melakukan semua penyaringan nama file. Daripada

find . -name "*.cpp" | grep "foo" | xargs grep 

melakukan

find . -name "*.cpp" -name "*foo*" -print0 | xargs -0 grep 

Jika Anda ingin melakukan sesuatu yang sedikit lebih rumit, sukai

find . -name "*.cpp" | egrep "foo|bar" | xargs grep 

Anda dapat melakukan

find . -name "*.cpp" "(" -name "*foo*" -o -name "*bar*" ")" -print0 | xargs -0 grep 

Perhatikan bahwa ini harus bekerja bahkan untuk file dengan baris baru di namanya.

Dan, jika Anda membutuhkan kekuatan ekspresi reguler penuh, Anda dapat menggunakan -regex.

Scott
sumber
2

Ini harus bekerja bahkan tanpa alat GNU:

#Find all C++ files that match a certain pattern and then search them
find . -name "*.cpp"  | grep "<name regex>" | perl -pe 's/\n/\0/' \
  | xargs -0 grep "<content regex>"

The perlpanggilan menggantikan jeda baris dengan karakter null, yang akan memungkinkan xargs -0untuk menafsirkan input pada basis per-line daripada basis per-spasi.

Dengan menggunakan GNU, Anda dapat menghapus perlpanggilan dan mengubahnya xargs -0 …kexargs -d "\n" …

Tidak punya perlatau GNU? Coba awk '{printf "%s%c", $0, 0}'saja.

Adam Katz
sumber
1
Ini mungkin tidak melakukan hal yang benar jika beberapa nama file menyertakan baris baru (kejadian yang agak tidak biasa, pasti, tetapi bukan tidak mungkin).
Dhag
@ Dhag memiliki poin yang valid tentang xargs -d "\n". Itu kejadian yang sangat tidak biasa, tetapi jika Anda tidak memiliki kendali atas data dan khawatir itu menjadi risiko keamanan, berhati-hatilah dengan ekspektasi keluaran.
Adam Katz