grep rekursif tidak mencantumkan kecocokan dari beberapa file

Aug 20 2020

Saya menggunakan GNU grep 3.4 untuk menemukan skrip yang berisi pola tertentu. Untuk ini, saya memanggil grepsecara rekursif seperti itu

grep -rin . -e "pattern" 

Polanya hanya sebuah kata, tanpa ekspresi reguler. Yang aneh adalah bahwa output tidak mencantumkan kejadian di beberapa file yang pasti berisi string.

Saya telah mencoba membuka file-file ini vimdan mencari di sana menggunakan /patterndan menemukan polanya. Pengkodean ditampilkan vimsebagai [dos:utf-8:]. Ketika saya menyalin baris dan menulisnya ke file baru, grepperintah di atas mencantumkannya dengan benar.

Mengapa tidak grepmencantumkan file asli?

Jawaban

1 RedGrittyBrick Aug 20 2020 at 20:08

Grep (atau setidaknya yang lebih tua) tidak mengerti UTF8. Jadi karakter yang disusun, titik pemenggalan, atau data tak terlihat lainnya dapat membuat grep tidak bersemangat.

Grep juga dipengaruhi oleh nilai $LC_ALL, $LC_CTYPE dan $ LANG.

Gunakan vim untuk menyimpan beberapa baris di sekitar kata yang gagal ditemukan oleh grep, lalu buat hexdump dari file contoh kecil itu. Anda mungkin akan melihat mengapa grep gagal

Anda juga dapat menggunakan perintah vim ( ga, g8dll) untuk memeriksa karakter tetapi hex dump mungkin lebih jelas

1 buddemat Aug 20 2020 at 22:28

Saya menemukan masalahnya (dengan bantuan jawaban lain). File 'grep' tidak menunjukkan output apa pun karena sebenarnya tidak utf-8dikodekan, tetapi utf-16be. Saya mempelajari ini menggunakan hexdump (kredit untuk @RedGrittyBrick):

hd file_for_which_grep_works_as_expected.txt

menghasilkan

00000000  20 20 20 20 50 61 74 74  65 72 6e 0a              |    Pattern.|
0000000c

sedangkan

hd file_for_which_grep_fails.txt

kembali

00000000  fe ff 00 50 00 61 00 74  00 74 00 65 00 72 00 6e  |...P.a.t.t.e.r.n|
00000010  00 0a                                             |..|
00000012

Jadi, periksa ulang pengkodean dengan

file -i file_for_which_grep_fails.txt

mengidentifikasinya sebagai text/plain; charset=utf-16be.

Saya gagal untuk mengenali bahwa yang utf-8ditunjukkan oleh vimsebenarnya adalah pengkodean buffer , bukan pengkodean file . Pelaksana :set fileencodingdi vimjuga ditampilkan dengan benar fileencoding=utf-16(ditemukan di sinihttps://superuser.com/a/28783/1210682).

Jadi, masalahnya adalah saya greptidak berfungsi pada utf-16file yang disandikan. Ini sudah dijelaskan di sini:https://superuser.com/a/231471/1210682. Namun, solusi untuk mengonversi utf-16file menjadi utf-8sebelumnya greptidak berfungsi ketika saya menggunakannya secara rekursif, karena saya tidak tahu sebelumnya file mana yang mungkin utf-8dan yang mana utf-16dan sedang mencari melalui banyak file.

Ada solusi yang berbeda, dua di antaranya akan saya jelaskan secara singkat di sini:

  1. Solusi cepat dan kotor yang berhasil bagi saya adalah memperluas pola pencarian untuk menyertakan pola yang cocok dengan utf-16versi dan mencari salah satu dari kedua pola tersebut:

    grep -riPa . -e "pattern|p.a.t.t.e.r.n."
    

    Ini tentu saja sangat terbatas dalam hal kemungkinan pola.

  2. Ada alternatif untuk greplike ugrepatau ripgrepyang (antara lain) bisa menangani utf-16file. Saya akhirnya menggunakan ripgrepyang tersedia di repositori paket Ubuntu standar dari 18.04 pada:

    rg -i "pattern"  
    

Ada diskusi bagus tentang alternatif di sini: https://stackoverflow.com/questions/3752913/grepping-binary-files-and-utf16, di antaranya pendekatan menarik yang mencoba mengubah pola pencarian menjadi utf-16dan memasukkannya ke grep. Namun, saya tidak bisa membuatnya bekerja.