Jalankan `grep` mengecualikan file di jalur tertentu


12

Saya ingin mengecualikan file ./test/main.cppdari pencarian saya.

Inilah yang saya lihat:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Saya tahu adalah mungkin untuk mendapatkan output yang saya inginkan dengan menggunakan beberapa perintah dalam pengaturan pipa-dan-filter, tetapi apakah ada beberapa kutipan / pelarian yang akan membuat grepmengerti apa yang saya inginkan secara asli?


Solusi yang didasarkan pada pemfilteran output tidak berskala dengan baik karena mencari file tanpa perlu sebelum mengecualikan hasil yang terkait. Masalah ini diperbesar jika saya ingin mengecualikan seluruh direktori (dengan --exclude-dir). Itu sebabnya saya ingin membuat grep melakukan pengecualian secara asli.
nobar

1
--exclude mencantumkan glob bukan jalan
PersianGulf

Jawaban:


6

grep tidak dapat melakukan ini untuk file dalam satu direktori tertentu jika Anda memiliki lebih banyak file dengan nama yang sama di direktori yang berbeda, gunakan find sebagai gantinya:

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+


Mengapa kamu melarikan diri \!dan \+? Tampaknya berfungsi dengan baik tanpa garis miring terbalik.
nobar

@ nobar Saya sudah terbiasa karena beberapa karakter adalah kata kunci shell sehingga Anda tidak akan pernah terkejut karena tidak ada yang bisa terjadi jika mereka lolos.
MichalH

" greptidak bisa melakukan ini, gunakan findsaja" - sempurna.
Nobar

4

Saya pikir itu tidak mungkin dengan GNU grep. Anda tidak perlu pipa.

Dengan find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

Dengan zsh:

grep pattern ./**/*~./test/main.cpp(.)

(mengecualikan file tersembunyi, sama baiknya untuk mengecualikan .git, .svn ...).


2

Saya bisa menulis buku: "Seni yang hilang xargs". The find ... -exec … ';peluncuran grep untuk setiap file (tapi varian dengan -exec … +tidak). Yah, kita membuang-buang siklus CPU hari ini jadi mengapa tidak, kan? Tetapi jika kinerja dan memori serta daya adalah masalah: gunakan xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

GNU find's -print0akan NUL-terminate output dan nya xargs' -0pilihan kehormatan format yang sebagai input. Ini memastikan karakter lucu apa pun yang dimiliki file Anda, saluran pipa tidak akan bingung. The -rpilihan memastikan tidak ada kesalahan dalam kasus findtemuan apa-apa.

Catatan, sekarang Anda dapat melakukan hal-hal seperti:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep -zmelakukan hal yang sama dengan xargs ' -0.


3
Beberapa catatan menarik, tapi saya tidak yakin Anda benar tentang masalah kinerja. Seperti yang saya mengerti itu find -exec (cmd) {} +berfungsi sama xargsdan find -exec (cmd) {} \;bekerja sama dengan xargs -n1. Dengan kata lain, pernyataan Anda hanya benar jika \;versi tersebut digunakan.
nobar

3
Perpipaan menjadi xargskurang efisien daripada menggunakan -exec … +(meskipun sedikit). Tidak ada jawaban yang disebutkan di sini -exec … \;.
Gilles 'SANGAT berhenti menjadi jahat'

1
Ya, s - t. Saya berkencan sendiri. Terima kasih atas komentar dan koreksi. Saya pikir \ + itu salah ketik. Oh, lihat, -exec ... +ditambahkan pada Januari 2005. Ya, saya tidak ketinggalan zaman ... sama sekali.
Otheus

2

Jika finddukungan Anda -pathyang ditambahkan ke POSIX pada 2008 tetapi masih hilang di Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +

1
Saya tidak berpikir itu akan berhasil karena bangsawan ingin main.cpp di direktori lain
Eric Renouf

1
tidakkah pola Anda mengecualikan main.cpp dari semua direktori lain juga? Itu tidak akan diinginkan
Eric Renouf

@EricRenouf: Oh, kesalahan saya, salah membaca. Memperbarui jawaban saya.
cuonglm

@Gilles: Mengapa -pathbukan POSIX?
cuonglm

Ah, maaf, kesalahan saya, sudah ditambahkan pada tahun 2008. Namun masih hilang dari Solaris.
Gilles 'SANGAT berhenti menjadi jahat'

1

Sebagai catatan, inilah pendekatan yang saya sukai:

grep pattern $(find . -type f ! -path './test/main.cpp')

Dengan menjaga grepdi awal perintah, saya pikir ini sedikit lebih jelas - plus itu tidak menonaktifkan greppenyorotan warna. Dalam arti tertentu, menggunakan findsubstitusi perintah hanyalah cara untuk memperluas / mengganti subset grepfungsi pencarian file (terbatas) .


Bagi saya, find -execsintaksinya agak misterius. Satu kompleksitas dengan find -execadalah (kadang-kadang) kebutuhan untuk melarikan diri dari berbagai karakter (terutama jika \;digunakan di bawah Bash). Hanya untuk tujuan menempatkan hal-hal ke dalam konteks yang akrab, dua perintah berikut pada dasarnya setara:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Jika Anda ingin mengecualikan subdirektori , mungkin perlu menggunakan wildcard. Saya tidak sepenuhnya memahami skema di sini - bicara tentang misterius :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Satu catatan lebih lanjut untuk menggeneralisasi findsolusi berbasis untuk digunakan dalam skrip : Baris grepperintah harus menyertakan opsi -H/ --with-filename. Kalau tidak, itu akan mengubah format output di bawah keadaan yang hanya ada satu nama file di hasil pencarian find. Ini penting karena tampaknya tidak perlu jika menggunakan greppencarian file asli (dengan -ropsi).

... Bahkan lebih baik, adalah memasukkan /dev/nullsebagai file pertama yang dicari. Ini memecahkan dua masalah:

  • Ini memastikan bahwa jika ada satu file untuk dicari, grepakan berpikir ada dua dan menggunakan mode keluaran multi-file.
  • Ini memastikan bahwa jika tidak ada file untuk dicari, grepakan berpikir ada satu file dan tidak menunggu di stdin.

Jadi jawaban akhirnya adalah:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')

Anda seharusnya tidak menggunakan output dari finddalam substitusi perintah. Ini rusak jika ada nama file yang berisi spasi atau karakter khusus lainnya. Gunakan find -exec, kuat dan mudah digunakan.
Gilles 'SO- stop being evil'

@Gilles: Poin yang sangat bagus - juga output mungkin dapat melebihi batas ukuran baris perintah dari beberapa program. Kaisar peringatan.
Nobar

Ugh. Sintaks 'temukan' sangat sulit. '-o' adalah operator "atau" (juga '-atau' di Linux), tetapi ini adalah penggunaan umum (misalnya dengan '-prune') tidak memetakan secara konseptual ke pengertian logis atau. Ini fungsional atau bukan logis atau.
Nobar

Cara lain untuk mengecualikan subdirektori berdasarkan pencocokan nama: find -iname "*target*" -or -name 'exclude' -prune. Yah, itu semacam berfungsi - direktori yang dipangkas akan terdaftar, tetapi tidak dicari. Jika Anda tidak ingin terdaftar, Anda dapat menambahkan semacam mubazir! -name 'exclude'
nobar
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.