Bagaimana cara mencari file di mana ada dua kata berbeda?

14

Saya sedang mencari cara untuk mencari file di mana ada dua contoh kata dalam file yang sama. Saya telah menggunakan yang berikut untuk melakukan pencarian saya hingga saat ini:

find . -exec grep -l "FIND ME" {} \;

Masalah yang saya temui adalah bahwa jika tidak ada satu ruang yang tepat antara "TEMUKAN" dan "AKU", hasil pencarian tidak menghasilkan file. Bagaimana cara saya mengadaptasi string pencarian sebelumnya di mana kedua kata "TEMUKAN" dan "AKU ada dalam file yang bertentangan dengan" TEMUKAN AKU "?

Saya menggunakan AIX.

Chad Harrison
sumber
1
Apakah kata-kata itu ada di mana saja dalam file, atau apakah mereka selalu berada di baris yang sama?
Sobrique
Maksudnya adalah garis yang sama.
Chad Harrison
Alternatif, jika kata-kata tersebut berada pada baris yang sama adalah dengan menggunakan ekspresi reguler dengan grep -E/ egrepyang menggambarkan semua pola yang Anda minati (dan menggunakan +alih-alih ;jika +
temuan

Jawaban:

21

Dengan alat GNU:

find . -type f  -exec grep -lZ FIND {} + | xargs -r0 grep -l ME

Anda dapat melakukannya secara standar:

find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;

Tapi itu akan menjalankan dua greps per file. Untuk menghindari menjalankan yang banyak grepdan masih dapat dibawa-bawa sementara masih memungkinkan karakter apa pun dalam nama file, Anda dapat melakukan:

convert_to_xargs() {
  sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
    {
      if (NR > 1) {
        printf "%s", line
        if (!index($0, "//")) printf "\\"
        print ""
      }
      line = $0
    }'
    END { print line }'
}

find .//. -type f |
  convert_to_xargs |
  xargs grep -l FIND |
  convert_to_xargs |
  xargs grep -l ME

Gagasannya adalah untuk mengubah output findmenjadi format yang cocok untuk xargs (yang mengharapkan blank (SPC / TAB / NL, dan blank lainnya dari lokal Anda dengan beberapa implementasi xargs) daftar kata-kata yang terpisah di mana single, double quotes dan backslash dapat melarikan diri kosong dan satu sama lain).

Secara umum Anda tidak dapat memposting proses keluaran find -print, karena memisahkan nama file dengan karakter baris baru dan tidak lepas dari karakter baris baru yang ditemukan dalam nama file. Sebagai contoh jika kita melihat:

./a
./b

Kami tidak punya cara untuk mengetahui apakah itu satu file yang dipanggil bdalam direktori yang disebut a<NL>.atau apakah itu dua file adan b.

Dengan menggunakan .//., karena //tidak dapat muncul sebaliknya di jalur file sebagai keluaran oleh find(karena tidak ada yang namanya direktori dengan nama kosong dan /tidak diizinkan dalam nama file), kita tahu bahwa jika kita melihat baris yang berisi //, maka itu baris pertama dari nama file baru. Jadi kita bisa menggunakan awkperintah itu untuk menghindari semua karakter baris baru tetapi yang mendahului baris-baris itu.

Jika kita mengambil contoh di atas, findakan menampilkan dalam kasus pertama (satu file):

.//a
./b

Awk yang lolos ke:

.//a\
./b

Sehingga xargsmelihatnya sebagai satu argumen. Dan dalam kasus kedua (dua file):

.//a
.//b

Yang awkakan pergi apa adanya, jadi xargsmelihat dua argumen.

Stéphane Chazelas
sumber
Mengapa tidak menggunakan find ... -print0dan grep --nullsebagai gantinya?
Dihancurkan
@razzed, tidak yakin apa maksud Anda itu. grep --null(alias -Z) digunakan pada yang pertama tetapi merupakan ekstensi GNU. -print0(ekstensi GNU lain) tidak akan membantu di sini.
Stéphane Chazelas
Terima kasih. Saya ingin membungkus kode shell Anda menjadi skrip yang mengambil direktori pencarian sebagai argumen dari baris perintah. Saya belum yakin apa .//.artinya, dan bertanya-tanya bagaimana saya bisa memodifikasi itu untuk menerima argumen dari baris perintah, katakan $1?
Tim
Terima kasih. Dalam perintah Anda, apakah perlu digunakan -print0dengan finddan -0dengan xargs?
Tim
@Tim, tidak yakin apa yang Anda maksud. Saya tidak menggunakan find -print0jawaban saya di mana pun.
Stéphane Chazelas
8

Jika file-file tersebut dalam satu direktori dan nama mereka tidak mengandung spasi, tab, baris baru, *, ?atau [karakter dan tidak mulai dengan -atau ., ini akan mendapatkan daftar file yang berisi ME, kemudian mempersempit yang turun ke orang-orang yang juga mengandung FIND.

grep -l FIND `grep -l ME *`
pengguna45529
sumber
INI membutuhkan lebih banyak upvote !! Jauh lebih anggun daripada jawaban "diterima". Bekerja untukku.
roblogic
Baru saja melakukan grep -l CategoryLinearAxis `grep -l labelJsFunction *`sambil mencari file yang memiliki kedua atribut di dalamnya. Sungguh cara yang sempurna untuk melakukannya. +1
WEBjuju
3

Dengan awkAnda juga bisa menjalankan:

find . -type f  -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;

Menggunakan cxdan cymenghitung untuk pencocokan garis FINDdan masing-masing ME. Di ENDblok, jika kedua penghitung> 0, itu mencetak FILENAME.
Ini akan lebih cepat / lebih efisien dengan gnu awk:

find . -type f  -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +
don_crissti
sumber
2

Atau gunakan egrep -eatau grep -Esuka ini:

find . -type f -exec egrep -le '(ME.*FIND|FIND.*ME)' {} \;

atau

find . -type f -exec grep -lE '(ME.*FIND|FIND.*ME)' {} +

Make +make find (jika didukung) menambahkan beberapa nama file (path) sebagai argumen pada perintah yang sedang -execdiedit. Ini menyimpan proses dan jauh lebih cepat daripada \;yang memanggil perintah satu kali untuk setiap file yang ditemukan.

-type f hanya cocok dengan file, untuk menghindari grepping pada direktori.

'(ME.*FIND|FIND.*ME)'adalah ekspresi reguler yang cocok dengan setiap baris yang mengandung "ME" diikuti oleh "FIND" atau "FIND" diikuti oleh "ME". (Kutipan tunggal untuk mencegah shell menafsirkan karakter khusus).

Tambahkan a -ike grepperintah untuk membuatnya case-sensitive.

Untuk hanya mencocokkan garis di mana "TEMUKAN" muncul sebelum "AKU", gunakan 'FIND.*ME'.

Untuk membutuhkan spasi (1 atau lebih, tetapi tidak ada yang lain) antara kata-kata: 'FIND +ME'

Untuk mengizinkan spasi (0 atau lebih, tetapi tidak ada yang lain) di antara kata-kata: 'FIND *ME'

Kombinasi ini tidak ada habisnya dengan ekspresi reguler, dan asalkan Anda tertarik untuk mencocokkan hanya berdasarkan baris-per-waktu, egrep sangat kuat.

MattBianco
sumber
Apakah sebagian besar greps tidak mendukung "-r"? Itu akan menghilangkan "find", tetapi mungkin ada soket atau file tidak biasa lainnya di pohon yang sedang dicari.
stolenmoment
OP menggunakan AIX dan punya finddalam pertanyaan.
MattBianco
0

Melihat jawaban yang diterima, tampaknya lebih kompleks daripada yang seharusnya. Versi GNU finddan grepdan xargsmendukung string yang diakhiri NULL. Sesederhana:

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME

Anda dapat memodifikasi findperintah Anda untuk memfilter ke file yang Anda inginkan, dan itu berfungsi dengan nama file yang mengandung karakter apa pun; tanpa menambahkan kompleksitas sedparsing. Jika Anda ingin memproses file lebih lanjut, tambahkan yang lain --nullke yang terakhirgrep

find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo

Dan, sebagai fungsi:

find_strings() {
    find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}

Jelas, gunakan jawaban yang diterima jika Anda tidak menjalankan versi GNU dari alat-alat ini.

diratakan
sumber
1
--null, --print0, -0Semua ekstensi GNU. Meskipun beberapa dari mereka ditemukan dalam implementasi lain saat ini, mereka masih tidak portabel dan tidak dalam standar POSIX atau Unix.
Stéphane Chazelas