Что заставляет grep считать файл двоичным?

185

У меня есть несколько дампов базы данных из системы Windows на моем компьютере. Это текстовые файлы. Я использую Cygwin, чтобы просмотреть их. Они выглядят как простые текстовые файлы; Я открываю их с помощью текстовых редакторов, таких как блокнот и WordPad, и они выглядят разборчиво. Тем не менее, когда я запускаю grep на них, он скажет binary file foo.txt matches.

Я заметил, что файлы содержат некоторые NULсимволы ascii , которые, как я считаю, являются артефактами из дампа базы данных.

Так что же заставляет grep считать эти файлы двоичными? NULХарактер? Есть ли флаг в файловой системе? Что мне нужно изменить, чтобы программа grep показала мне совпадения строк?

user394
источник
2
--null-dataможет быть полезно, если NULэто разделитель.
Стив

Ответы:

126

Если NULв файле есть символ, grep будет рассматривать его как двоичный файл.

Подобный обходной путь может сначала cat file | tr -d '\000' | yourgrepустранить все нули, а затем выполнить поиск по файлу.

bbaja42
источник
149
... или используйте -a/ --text, по крайней мере, с GNU grep.
Дероберт
1
@derobert: на самом деле, в некоторых (более старых) системах grep видит строки, но его вывод будет обрезать каждую совпадающую строку вначале NUL(вероятно, потому, что он вызывает printf C и дает ей совпадающую строку?). В такой системе a grep cmd .sh_historyвернет столько пустых строк, сколько строк соответствует 'cmd', поскольку каждая строка sh_history имеет определенный формат с NULначалом в начале каждой строки. (но ваш комментарий "по крайней мере, по GNU grep", вероятно, сбывается. У меня сейчас нет ни одного под рукой для тестирования, но я ожидаю, что они справятся с этим красиво)
Оливье Дюлак
4
Является ли наличие NUL-символа единственным критерием? Я сомневаюсь. Это, вероятно, умнее, чем это. Я думаю, что-нибудь, выходящее за пределы диапазона Ascii 32-126, но мы должны были бы взглянуть на исходный код, чтобы быть уверенными.
Майкл Мартинес
2
Моя информация была из справочной страницы конкретного экземпляра grep. Ваш комментарий о реализации действителен, источник превосходит документы.
bbaja42
2
У меня был файл, который grepна cygwin рассматривал двоичный файл, потому что он имел длинную черту (0x96) вместо обычного дефиса / минус ASCII (0x2d). Полагаю, этот ответ решил проблему ОП, но, похоже, он неполон.
cp.engr
121

grep -a работал на меня:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text
Plouff
источник
4
Это лучший, наименее дорогой ответ ИМО.
pydsigner
Но не совместим с POSIX
Маттео
21

Вы можете использовать stringsутилиту для извлечения текстового содержимого из любого файла , а затем по конвейеру через grep, например: strings file | grep pattern.

holgero
источник
2
Идеально подходит для поиска файлов журналов, которые могут быть частично повреждены
Ханнес Р.
да, иногда бинарное смешанное ведение журнала также происходит. Это хорошо.
sdkks
13

GNU grep 2.24 RTFS

Вывод: только в 2 и 2 случаях:

  • NULнапример, printf 'a\0' | grep 'a'

  • ошибка кодирования в соответствии с C99 mbrlen(), например:

    export LC_CTYPE='en_US.UTF-8'
    printf 'a\x80' | grep 'a'
    

    потому что \x80не может быть первым байтом точки Unicode UTF-8 : UTF-8 - Описание | en.wikipedia.org

Кроме того, как уже упоминал Стефан Шазелас, что заставляет grep считать файл двоичным? | Unix и Linux Stack Exchange , эти проверки выполняются только до первого чтения буфера длины TODO.

Только до первого чтения буфера

Таким образом, если в середине очень большого файла возникает ошибка NUL или кодировки, она может быть в любом случае обработана.

Я полагаю, это из соображений производительности.

Например: это печатает строку:

printf '%10000000s\n\x80a' | grep 'a'

но это не

printf '%10s\n\x80a' | grep 'a'

Фактический размер буфера зависит от того, как файл читается. Например, сравнить:

export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'

С помощью sleepпервой строки передается grep, даже если длина ее составляет всего 1 байт, потому что процесс переходит в спящий режим, а второе чтение не проверяет, является ли файл двоичным.

RTFS

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24

Найдите, где закодировано сообщение об ошибке stderr:

git grep 'Binary file'

Приводит нас к /src/grep.c:

if (!out_quiet && (encoding_error_output
                    || (0 <= nlines_first_null && nlines_first_null < nlines)))
    {
    printf (_("Binary file %s matches\n"), filename);

Если эти переменные были хорошо названы, мы в основном пришли к выводу.

encoding_error_output

Быстрый поиск encoding_error_outputпоказывает, что единственный путь кода, который может изменить его, проходит buf_has_encoding_errors:

clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
  return true;

тогда просто man mbrlen.

nlines_first_null и nlines

Инициализировано как:

intmax_t nlines_first_null = -1;
nlines = 0;

поэтому, когда ноль найден, 0 <= nlines_first_nullстановится истиной.

TODO когда может nlines_first_null < nlinesбыть ложным? Мне стало лень.

POSIX

Не определяет бинарные параметры grep - поиск файла для шаблона | pubs.opengroup.org , а GNU grep не документирует это, поэтому RTFS - единственный путь.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
источник
1
Впечатляющая экспликация!
user394
2
Обратите внимание, что проверка на действительный UTF-8 происходит только в локалях UTF-8. Также обратите внимание, что проверка выполняется только при первом чтении буфера из файла, который для обычного файла в моей системе составляет 32768 байт, но для канала или сокета может быть всего один байт. Сравните (printf '\n\0y') | grep yс, (printf '\n'; sleep 1; printf '\0y') | grep yнапример.
Стефан Шазелас
@ StéphaneChazelas «Обратите внимание, что проверка на действительный UTF-8 происходит только в локалях UTF-8»: вы имеете в виду, export LC_CTYPE='en_US.UTF-8'как в моем примере, или что-то еще? Buf read: удивительный пример, добавлен в ответ. Вы, очевидно, читали источник больше, чем я, напоминает мне о тех хакерских коанах "Ученик был просветленным" :-)
Сиро Сантилли 新疆 改造 中心 法轮功 六四 事件
1
Я не стал вдаваться в подробности, но сделал это совсем недавно
Стефан Шазелас
1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功 с какой версией GNU grep вы тестировали?
jrw32982
6

Grep: один из моих текстовых файлов внезапно стал двоичным

$ file foo.txt
foo.txt: ISO-8859 text

Решением было преобразовать его, используя iconv:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
zzapper
источник
1
Это случилось и со мной. В частности, причиной был неразрывный пробел в кодировке ISO-8859-1, который мне пришлось заменить обычным пробелом, чтобы заставить grep искать в файле.
Gallaecio
4
grep 2.21 обрабатывает текстовые файлы ISO-8859 так, как если бы они были двоичными, добавьте экспорт LC_ALL = C перед командой grep.
netawater
@netawater Спасибо! Это, например, случай, если у вас есть что-то вроде Мюллера в текстовом файле. Это 0xFCшестнадцатеричное, поэтому за пределами диапазона grep будет ожидать utf8 (до 0x7F). Проверьте с помощью printf 'a \ x7F' | grep 'a', как Сиро описал выше.
Анн ван Россум
5

Файл /etc/magicили /usr/share/misc/magicсодержит список последовательностей, которые команда fileиспользует для определения типа файла.

Обратите внимание, что двоичный файл может быть просто запасным решением. Иногда файлы со странной кодировкой также считаются двоичными.

grepв Linux есть несколько опций для обработки двоичных файлов, таких как --binary-filesили-U / --binary

klapaucius
источник
Точнее, ошибка кодирования в соответствии с C99 mbrlen(). Пример и исходная интерпретация по адресу: unix.stackexchange.com/a/276028/32558
Сиро Сантилли, 12 апреля 16 августа
2

У одного из моих учеников была эта проблема. Есть ошибка grepв Cygwin. Если файл содержит символы, отличные от символов Ascii, grepи egrepсчитайте его двоичным.

Джоан Понтий
источник
Это звучит как функция, а не ошибка. Особенно с учетом того, что есть опция командной строки для управления им (-a / --text)
Уилл Шеппард
2

На самом деле, отвечая на вопрос «Что заставляет grep считать файл двоичным?», Вы можете использовать iconv:

$ iconv < myfile.java
iconv: (stdin):267:70: cannot convert

В моем случае были испанские символы, которые правильно отображались в текстовых редакторах, но grep считал их двоичными; iconvвывод указал мне на номера строк и столбцов этих символов

В случае NULсимволов iconvбудет считать их нормальными и не будет выводить такой вывод, поэтому этот метод не подходит

golimar
источник
1

У меня такая же проблема. Я привык vi -b [filename]видеть добавленных персонажей. Я нашел контрольные символы ^@и ^M. Затем в vi введите :1,$s/^@//gдля удаления ^@символов. Повторите эту команду для ^M.

Предупреждение: чтобы получить «синие» управляющие символы, нажмите Ctrl+, vзатем Ctrl+ Mили Ctrl+ @. Затем сохраните и выйдите из vi.

Точно сказать не могу
источник