Bagaimana cara mengabaikan nama file tertentu menggunakan "find"?

143

Salah satu perintah BASH favorit saya adalah:

find . -name '*.*' -exec grep 'SearchString' {} /dev/null \;

yang mencari konten semua file di dan di bawah direktori saat ini untuk SearchString yang ditentukan. Sebagai seorang pengembang, ini kadang-kadang berguna.

Karena proyek saya saat ini, dan struktur basis kode saya, saya ingin membuat perintah BASH ini lebih maju dengan tidak mencari file apa pun yang ada di atau di bawah direktori yang berisi ".svn", atau file apa pun yang akhiri dengan ".html"

Halaman MAN untuk menemukan agak membingungkan saya. Saya mencoba menggunakan -prune, dan itu memberi saya perilaku aneh. Dalam upaya untuk melewati hanya halaman .html (untuk memulai), saya mencoba:

find . -wholename './*.html' -prune -exec grep 'SearchString' {} /dev/null \;

dan tidak mendapatkan perilaku yang saya harapkan. Saya pikir saya mungkin kehilangan titik-proune. Bisakah kalian membantu saya?

Terima kasih

Cody S
sumber
1
Just fyi: findbukan perintah built-in bash tetapi program terpisah
WakiMiko
1
Anda dapat mencari di dalam file dengangrep -rl 'SearchString'
emanuele
@emanuele Hai, selamat datang di SuperUser (dan jaringan Stack Exchange). Ini adalah pertanyaan yang saya ajukan, dan itu dijawab, 2 1/2 tahun yang lalu. Biasanya, jika Anda ingin menambahkan jawaban pada pertanyaan, silakan lakukan dengan menggulir ke bawah dan menjawab di sana, alih-alih dalam komentar. Karena pertanyaan ini sudah memiliki jawaban yang diterima (yang memiliki tanda centang hijau), tidak mungkin jawaban Anda akan mendapat banyak perhatian. FYI.
Cody S
1
Hai, ini bukan jawaban untuk pertanyaan Anda. Ini hanya tip, seperti yang Anda nyatakan dalam Pembukaan yang digunakan finduntuk mencari di dalam file.
emanuele
2
FWIW, -name '*.*'tidak menemukan semua file: hanya yang dengan .nama mereka (penggunaan *.*biasanya adalah DOS-isme, sedangkan di Unix, Anda biasanya menggunakan hanya *untuk itu). Untuk benar-benar cocok dengan mereka semua, hanya menghapus argumen sama sekali: find . -exec .... Atau jika Anda hanya ingin menerapkan grep ke file (dan lewati direktori) maka lakukan find . -type f -exec ....
Stefan

Jawaban:

197

Anda dapat menggunakan fitur negate (!) Find untuk tidak mencocokkan file dengan nama tertentu:

find . ! -name '*.html' ! -path '*.svn*' -exec grep 'SearchString' {} /dev/null \;

Jadi jika nama berakhir dengan .html atau mengandung .svn di mana saja di jalan, itu tidak akan cocok, dan karenanya eksekutif tidak akan dieksekusi.

Paul
sumber
1
Haruskah saya masih menentukan -nama ' . 'di suatu tempat di sana? Apakah saya akan melakukannya sebelum, atau setelah negasi?
Cody S
Apakah maksud *.*kecocokan Anda untuk memastikan hanya kecocokan file yang mengandung .? Temukan akan cocok dengan semua file dengan tidak adanya namearahan, sehingga di atas akan cocok dengan semuanya kecuali html dan svn
Paul
5
Saya pikir Anda ingin -wholename '*.svn*'daripada -name.
fuenfundachtzig
2
Ya, itu benar, sehingga .svndirektori dikecualikan dari hasil pencarian.
fuenfundachtzig
1
@Noumenon ! -name '.'harus mengecualikan .dari hasil pencarian.
Paul
11

Saya sudah memiliki masalah yang sama untuk waktu yang lama, dan ada beberapa solusi yang dapat diterapkan dalam situasi yang berbeda:

  • ack-grepadalah semacam "pengembang grep" yang secara default melompati direktori kontrol versi dan file sementara. The manHalaman menjelaskan bagaimana untuk mencari hanya jenis file tertentu dan bagaimana menentukan sendiri .
  • grepOpsi --excludedan miliknya sendiri --exclude-dirdapat digunakan dengan sangat mudah untuk melewati gumpalan file dan direktori tunggal (sayangnya, tidak ada globbing untuk direktori).
  • find . \( -type d -name '.svn' -o -type f -name '*.html' \) -prune -o -print0 | xargs -0 grep ... harus bekerja, tetapi opsi di atas mungkin tidak terlalu merepotkan dalam jangka panjang.
l0b0
sumber
9

findPerintah berikut tidak memangkas direktori yang namanya berisi .svn , Meskipun tidak turun ke direktori, nama path yang dipangkas dicetak ... ( -name '*.svn'adalah penyebabnya!) ..

Anda dapat memfilter nama direktori melalui: grep -d skipyang secara diam-diam melewatkan "nama direktori" input tersebut.

Dengan GNU grep, Anda dapat menggunakan -Hbukan /dev/null. Sebagai masalah sampingan: \+bisa jauh lebih cepat daripada \;, misalnya. untuk 1 juta file satu baris, menggunakannya \;butuh 4m20s , \+hanya butuh 1,2s .

Metode berikut menggunakan xargsalih-alih -exec, dan mengasumsikan tidak ada baris baru \ndalam nama file Anda . Seperti yang digunakan di sini, xargssama dengan find \+.

xargsdapat meneruskan nama-file yang berisi spasi berurutan dengan mengubah pembatas input menjadi '\n'dengan -dopsi.

Ini mengecualikan direktori yang namanya hanya berisi .svn dan menggerogoti file yang tidak diakhiri .html.

find . \( -name '*.svn*' -prune  -o ! -name '*.html' \) |
   xargs -d '\n' grep -Hd skip 'SearchString'
Peter.O
sumber
1
Terima kasih telah menunjukkan \+varian aksi -exec. Hore untuk masalah sampingan ringan!
Christian Long
Tentu saja, karena +ini bukan karakter khusus untuk shell, Anda tidak perlu mengetik \sebelumnya.
Scott