рекурсивный grep не отображает совпадения из некоторых файлов

Aug 20 2020

Я использую GNU grep 3.4 для поиска скриптов, содержащих определенный шаблон. Для этого я grepрекурсивно вызываю так

grep -rin . -e "pattern" 

Шаблон - это просто слово, а не регулярное выражение. Странно то, что в выводе не перечислены вхождения в некоторых файлах, которые определенно содержат строку.

Я попытался открыть эти файлы vimи выполнить поиск там, /patternи он нашел шаблон. Кодировка отображается vimкак [dos:utf-8:]. Когда я копирую строку и записываю ее в новый файл, приведенная выше grepкоманда отображает ее правильно.

Почему не grepуказан исходный файл?

Ответы

1 RedGrittyBrick Aug 20 2020 at 20:08

Grep (или, по крайней мере, более старые) не понимают UTF8. Таким образом, составные символы, точки переноса или другие невидимые данные могут отпугнуть grep.

Grep также зависит от значений $LC_ALL, $LC_CTYPE и $ LANG.

Используйте vim, чтобы сохранить несколько строк вокруг слова, которое grep не удалось найти, а затем сделайте шестнадцатеричный дамп этого крошечного файла примера. Вы, наверное, поймете, почему grep не удалось

Вы также можете использовать команды vim ( gaи g8т. Д.) Для проверки символов, но шестнадцатеричный дамп может быть более понятным

1 buddemat Aug 20 2020 at 22:28

Я нашел проблему (с помощью другого ответа). Файлы 'grep' не выводили никаких результатов, потому что на самом деле они не были utf-8закодированы, но utf-16be. Я узнал об этом с помощью hexdump (кредиты @RedGrittyBrick):

hd file_for_which_grep_works_as_expected.txt

уступил

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

в то время как

hd file_for_which_grep_fails.txt

вернулся

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

Итак, дважды проверив кодировку с помощью

file -i file_for_which_grep_fails.txt

идентифицировал это как text/plain; charset=utf-16be.

Я не в состоянии признать , что utf-8показано vimбыло на самом деле буфер кодирования, а не файл кодирования. Выполнение :set fileencodingв vimтакже отображается правильно fileencoding=utf-16(здесьhttps://superuser.com/a/28783/1210682).

Итак, проблема в том, что my grepне работает с utf-16закодированными файлами. Это уже было описано здесь:https://superuser.com/a/231471/1210682. Тем не менее, средство преобразования utf-16файлов , utf-8прежде чем grepне работает , когда я использую его рекурсивно, так как я заранее не знаю , какие файлы могут быть utf-8и которые utf-16и утра поиск через много файлов.

Существуют разные решения, два из которых я кратко опишу здесь:

  1. Быстрый и грязный раствор , который работал для меня было расширить шаблон поиска , чтобы включить тот , который будет соответствовать utf-16версии и поиск по одной из двух моделей:

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

    Конечно, это очень ограничено с точки зрения возможных шаблонов.

  2. Есть альтернативы , grepкак ugrepили ripgrepчто (среди прочего) может обрабатывать utf-16файлы. В итоге я использовал тот, ripgrepкоторый доступен в стандартных репозиториях пакетов Ubuntu с 18.04:

    rg -i "pattern"  
    

Здесь есть отличное обсуждение альтернатив: https://stackoverflow.com/questions/3752913/grepping-binary-files-and-utf16, среди них интересный подход, пытающийся преобразовать шаблон поиска utf-16в grep. Однако я не мог заставить его работать.