Bagaimana cara mengecualikan beberapa file yang tidak cocok dengan ekstensi tertentu dengan grep?

8

Saya ingin menampilkan semua baris yang mengandung kata OKsecara rekursif dari direktori. Tetapi ada beberapa ekstensi yang harus saya kecualikan dari hasilnya:

*~
*.map
*.js except *.debug.js

Saya mencoba:

grep -r --exclude={*~,*.map} "OK" /some/dir

Kecuali saya tidak tahu cara menghapus dari hasil semua .jsfile non-debug itu .

Pertanyaan Melimpah
sumber

Jawaban:

7

Saya hanya akan melewati satu detik grepuntuk menghapusnya:

grep -r --exclude={\*~,\*.map} "OK" bar/ | grep -vP '(?<!debug)\.js'

The -vmembalikkan pertandingan, mencetak baris yang tidak cocok dengan pola dan -Pmemungkinkan Perl Regular Expressions Kompatibel yang membiarkan kita menggunakan lookbehinds negatif . Regex khusus ini, akan cocok dengan .jsyang tidak diawali dengan debugcara yang berarti (karena kami membalikkan pertandingan) bahwa hanya .jsfile - file itu akan dicetak.

Namun, seperti yang ditunjukkan oleh @QuestionOverflow di dalam komentar, itu bisa memiliki efek samping yang tidak diinginkan dengan memfilter garis yang mengandung OKdan jskarena grep -vditerapkan pada seluruh output, bukan hanya nama file. Untuk menghindarinya, tambahkan saja titik dua (itulah yang grepdigunakan untuk memisahkan nama file dari konten file):

grep -r --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js:'

Itu masih akan gagal jika baris input Anda berisi foo.js:atau jika nama file Anda berisi :. Jadi, yang pasti, gunakan pendekatan yang berbeda:

grep -Tr --exclude={*~,*.map} "OK" bar/ | grep -vP '(?<!debug).js\t'

The -Tmenyebabkan grepuntuk mencetak tab antara nama file dan isi file. Jadi, jika kita cukup menambahkan a \tke akhir regex, itu hanya akan cocok dengan nama file, dan bukan isi baris.

Namun, menggunakanfind mungkin lebih masuk akal terlepas dari itu.

terdon
sumber
1
Apakah saya akan secara tidak sengaja mengecualikan baris dalam file yang saya inginkan, tetapi mengandung keduanya OKdan .jspada baris yang sama?
Pertanyaan Overflow
@QuestionOverflow ah, ya memang, tangkapan yang bagus. Lihat jawaban yang diperbarui.
terdon
Jawaban yang fantastis. Harus menerima milikmu karena aku meminta grep secara khusus. Terima kasih.
Pertanyaan Overflow
@QuestionOverflow Anda sangat menyambut. Secara umum, findmungkin lebih baik untuk hal semacam ini. Mendapatkan yang benar grepbisa rumit seperti yang Anda tunjukkan :).
terdon
Solusi Anda gagal jika seseorang memiliki failglobopsi yang ditetapkan di shell: bash: no match: --exclude=*~ Anda perlu mengutip argumen pola GLOB Anda --excludeuntuk menyembunyikannya dari ekspansi shell, misalnya--exclude={\*~,\*.map}
Ian D. Allen
7

Saya akan gunakan finduntuk mencari file dan menyalurkan hasilnya melalui xargs:

$ find . -type f \! -name "*~" \
                 \! -name "*.map" \
                 \! \( -name "*.js" -and \! -name "*.debug.js" \) \
         -print0 | xargs -0 grep "OK"

Ini mencari setiap file yang tidak cocok dengan " *~", " *.map" atau " *.jstetapi tidak *.debug.js".

Menggunakan findAnda dapat dengan mudah mencari aturan yang agak rumit dan pendekatan ini menyelamatkan Anda dari penghapusan positif palsu secara tidak sengaja seperti yang dapat terjadi dengan ganda grep.

Andreas Wiese
sumber
Jawaban yang bagus juga :)
Question Overflow
3
Ya, ini mungkin cara terbaik, +1. Anda juga bisa menggunakan -exec grep OK {} +alih-alih xargsdan menghindari program tambahan.
terdon
2
@IDSemua tidak, perhatikan bahwa saya menyarankan -exec +tidak -exec \;, yang akan menjalankan perintah sesedikit mungkin, seperti xargs.
terdon
4

Dengan zshAnda dapat melakukan:

setopt extendedglob
grep OK some/dir/**/^(*~|*.map|(^*debug).js)

Asalkan tentu saja daftar argumen tidak terlalu panjang, dalam hal ini Anda selalu dapat melakukannya:

printf '%s\0' some/dir/**/^(*~|*.map|(^*debug).js) | xargs -0 grep OK
Graeme
sumber
Juga, Anda dapat membuat yang terakhir zsh-hanya: autoload zargsdanzargs some/dir/**/^(*~|*.map|(^*debug).js) -- grep OK
don_crissti
2

Jika Anda tidak keberatan melihat output sedikit rusak (jika Anda melakukannya, Anda dapat mengurutkannya):

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir **/*.debug.js

Ini mensyaratkan bahwa shell Anda mendukung **untuk globbing rekursif: zsh tidak di luar kotak, bash tidak setelah Anda menjalankan shopt -s globstar, ksh93 tidak setelah Anda menjalankan set -o globstar.

Tanpa **dukungan di shell, Anda dapat menggunakan dua perintah grep:

grep -r --exclude={*~,*.map,*.js} "OK" /some/dir
grep -r --include=*.debug.js "OK" /some/dir
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Shell saya mendukung **, tetapi tampaknya ada sesuatu yang salah dengan argumen tambahan **/*.debug.js, menyebabkan grep diartikan OKsebagai direktori. Sudahkah Anda mencoba menjalankannya?
Pertanyaan Overflow
@QuestionOverflow Kesalahan saya, saya bertukar urutan argumen.
Gilles 'SO- stop being evil'
2

Anda bisa menggunakannya ripgrep. Secara default ia mengabaikan file yang tersembunyi dan menghormati .gitignorefile Anda .

Anda dapat menentukan aturan inklusi atau pengecualian dengan menggunakan parameter berikut:

-g/ --glob GLOBSertakan atau kecualikan file dan direktori untuk pencarian yang cocok dengan bola yang diberikan.

-t/ --type TYPEHanya mencari file yang cocok dengan JENIS. Beberapa jenis bendera mungkin disediakan.

-T/ --type-not TYPEJangan mencari file yang cocok dengan TYPE.

Gunakan --type-listbendera untuk mendaftar semua jenis yang tersedia.

Berikut adalah beberapa contoh sederhana:

rg -Tjs "OK"                              # Excludes *.js, *.jsx, *.vue files.
rg -tpy "OK"                              # Includes Python files.
rg --type-add 'map:*.map' -tmap PATTERN   # Excludes *.map files.
rg -g '!*.js' -g '*.debug.js' PATTERN     # Excludes *.js apart of *.debug.js.

Berikut adalah solusi lengkap untuk mengecualikan *.~, *.map, *.js, tapi tidak *.debug.js:

rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' "OK"

Pengujian:

$ touch file.~ file.map file.js file.debug.js file.txt file.md
$ rg --files
file.debug.js
file.js
file.map
file.md
file.txt
$ rg -g '*.*' -g '!*.~' -g '!*.map' -g '!*.js' -g '*.debug.js' --files
file.debug.js
file.md
file.txt
kenorb
sumber