Co sprawia, że ​​grep uważa plik za binarny?


185

Mam zrzuty bazy danych z systemu Windows na moim urządzeniu. Są to pliki tekstowe. Używam cygwina, by się przez nie przeszukiwać. Wyglądają na zwykłe pliki tekstowe; Otwieram je za pomocą edytorów tekstu, takich jak Notatnik i WordPad, i wyglądają czytelnie. Jednak gdy uruchomię na nich grep, powie binary file foo.txt matches.

Zauważyłem, że pliki zawierają niektóre NULznaki ascii , które moim zdaniem są artefaktami ze zrzutu bazy danych.

Co sprawia, że ​​grep uważa te pliki za binarne? NULCharakter? Czy w systemie plików jest flaga? Co muszę zmienić, aby grep wyświetlał mi dopasowania linii?


2
--null-datamoże być przydatny, jeśli NULjest separatorem.
Steve-o

Odpowiedzi:


125

Jeśli NULgdzieś w pliku jest znak, grep uzna go za plik binarny.

Może istnieć takie obejście, cat file | tr -d '\000' | yourgrepaby najpierw wyeliminować wszystkie wartości null, a następnie przeszukać plik.


149
... lub użyj -a/ --text, przynajmniej z GNU grep.
derobert

1
@derobert: w niektórych (starszych) systemach grep widzi linie, ale jego wynik obcina każdą pasującą linię na początku NUL(prawdopodobnie powoduje to, że wywołuje printf C i nadaje mu dopasowaną linię?). W takim systemie a grep cmd .sh_historyzwróci tyle pustych linii, ile jest linii pasujących do „cmd”, ponieważ każda linia sh_history ma określony format z NULna początku każdej linii. (ale twój komentarz „przynajmniej na temat GNU grep” prawdopodobnie się sprawdza. Nie mam go teraz pod ręką do przetestowania, ale spodziewam się, że poradzą sobie z tym dobrze)
Olivier Dulac

4
Czy obecność znaku NUL jest jedynym kryterium? Wątpię. Prawdopodobnie jest to mądrzejsze. Domyślam się, że wszystko, co wykracza poza zakres Ascii 32-126, musieliśmy spojrzeć na kod źródłowy, aby się upewnić.
Michael Martinez,

2
Moje informacje pochodziły ze strony man konkretnego wystąpienia grep. Twój komentarz na temat implementacji jest poprawny, źródło przebija dokumenty.
bbaja42,

2
Miałem plik, który grepna cygwin uważał za binarny, ponieważ miał długi myślnik (0x96) zamiast zwykłego myślnika / znaku minus ASCII (0x2d). Myślę, że ta odpowiedź rozwiązała problem PO, ale wygląda na to, że jest niekompletny.
cp.engr

121

grep -a pracował dla mnie:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text

4
To najlepsza, najtańsza odpowiedź IMO.
pydsigner

Ale niezgodne z POSIX
Matteo

21

Można użyć stringsnarzędzia, aby wyodrębnić zawartość tekstu z dowolnego pliku, a następnie rury to przez grepcoś takiego: strings file | grep pattern.


2
Idealny do grepowania plików dziennika, które mogą być częściowo uszkodzone
Hannes R.

tak, czasami zdarza się również, że binarne mieszane rejestrowanie. To jest dobre.
sdkks

13

GNU grep 2.24 RTFS

Wniosek: tylko 2 i 2 przypadki:

  • NULnp printf 'a\0' | grep 'a'

  • błąd kodowania zgodnie z C99 mbrlen(), np .:

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

    ponieważ \x80nie może być pierwszym bajtem punktu Unicode UTF-8 : UTF-8 - Opis | en.wikipedia.org

Co więcej, jak wspomniał Stéphane Chazelas. Co sprawia, że ​​grep uważa plik za plik binarny? | Wymiana stosów Unix i Linux , kontrole są wykonywane tylko do pierwszego odczytu bufora o długości TODO.

Tylko do pierwszego odczytanego bufora

Jeśli więc wystąpi błąd NUL lub błąd kodowania w środku bardzo dużego pliku, może on zostać i tak grepowany.

Wyobrażam sobie, że dzieje się tak ze względu na wydajność.

Np .: drukuje linię:

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

ale to nie:

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

Rzeczywisty rozmiar bufora zależy od sposobu odczytu pliku. Np. Porównaj:

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

W przypadku sleeppierwszego wiersza przechodzi do grep, nawet jeśli ma on tylko 1 bajt długości, ponieważ proces przechodzi w tryb uśpienia, a drugi odczyt nie sprawdza, czy plik jest binarny.

RTFS

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

Znajdź, gdzie jest zakodowany komunikat o błędzie Stderr:

git grep 'Binary file'

Prowadzi nas do /src/grep.c:

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

Jeśli te zmienne były dobrze nazwane, zasadniczo doszliśmy do wniosku.

encoding_error_output

Szybkie grepping dla encoding_error_outputpokazuje, że jedyna ścieżka kodu, która może go modyfikować, przechodzi buf_has_encoding_errors:

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

to po prostu man mbrlen.

nlines_first_null i nlines

Zainicjowany jako:

intmax_t nlines_first_null = -1;
nlines = 0;

więc po znalezieniu wartości null 0 <= nlines_first_nullstaje się prawdą.

DO ZROBIENIA, kiedy może nlines_first_null < nlinesbyć fałsz? Zrobiłem się leniwy

POSIX

Nie definiuje opcji binarnych grep - wyszukaj plik w poszukiwaniu wzorca | pubs.opengroup.org , a GNU grep tego nie dokumentuje, więc RTFS jest jedynym sposobem.


1
Imponujące wyjaśnienie!
użytkownik394

2
Pamiętaj, że sprawdzenie poprawności UTF-8 odbywa się tylko w ustawieniach regionalnych UTF-8. Zauważ też, że sprawdzanie odbywa się tylko na pierwszym buforze odczytanym z pliku, który dla zwykłego pliku wydaje się mieć 32768 bajtów w moim systemie, ale dla potoku lub gniazda może być tak mały jak jeden bajt. Porównać (printf '\n\0y') | grep yz (printf '\n'; sleep 1; printf '\0y') | grep ynp.
Stéphane Chazelas

@ StéphaneChazelas „Pamiętaj, że sprawdzenie poprawności UTF-8 odbywa się tylko w ustawieniach regionalnych UTF-8”: czy masz na myśli to, export LC_CTYPE='en_US.UTF-8'co w moim przykładzie, czy coś innego? Przeczytaj Buf: niesamowity przykład, dodany do odpowiedzi. Najwyraźniej przeczytałeś źródło bardziej niż ja, przypomina mi te hakerskie koany „Student został oświecony” :-)
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

1
Nie przyjrzałem się również szczegółom, ale zrobiłem to niedawno
Stéphane Chazelas,

1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功 przeciwko jakiej wersji GNU grep testowałeś?
jrw32982

6

Jeden z moich plików tekstowych został nagle uznany przez grep za binarny:

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

Rozwiązaniem było przekonwertowanie go za pomocą iconv:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt

1
To mi się też przydarzyło. W szczególności przyczyną była niezniszczalna przestrzeń zakodowana w ISO-8859-1, którą musiałem zastąpić zwykłą spacją, aby grep przeszukiwał plik.
Gallaecio,

4
grep 2.21 traktuje pliki tekstowe ISO-8859 tak, jakby były binarne, dodaj polecenie export LC_ALL = C przed poleceniem grep.
netawater

@netawater Thanks! Dzieje się tak np. W przypadku, gdy w pliku tekstowym znajduje się coś takiego jak Müller. Jest to liczba 0xFCszesnastkowa, więc poza zakresem grep oczekiwałby od utf8 (do 0x7F). Sprawdź za pomocą printf 'a \ x7F' | grep 'a' jak opisano powyżej Ciro.
Anne van Rossum,

5

Plik /etc/magiclub /usr/share/misc/magiczawiera listę sekwencji fileużywanych przez polecenie do określania typu pliku.

Zauważ, że plik binarny może być rozwiązaniem zastępczym. Czasami pliki z dziwnym kodowaniem są również uważane za binarne.

grepw systemie Linux ma kilka opcji do obsługi plików binarnych, takich jak --binary-fileslub-U / --binary


Mówiąc dokładniej, błąd kodowania zgodny z C99 mbrlen(). Przykład i interpretacja źródła na: unix.stackexchange.com/a/276028/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件 12.04.16

2

Jeden z moich uczniów miał ten problem. Wystąpił błąd grepw Cygwin. Jeśli plik ma znaki inne niż Ascii, grepi egrepzobacz go jako plik binarny.


To brzmi jak funkcja, a nie błąd. Zwłaszcza, że ​​istnieje opcja wiersza polecenia do kontrolowania go (-a / --text)
Will Sheppard

2

Odpowiadając na pytanie „Co sprawia, że ​​grep uważa plik za plik binarny?”, Możesz użyć iconv:

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

W moim przypadku były hiszpańskie znaki, które poprawnie wyświetlały się w edytorach tekstu, ale grep uważał je za binarne; iconvwynik wskazywał na numery wierszy i kolumn tych znaków

W przypadku NULznaków iconvuzna je za normalne i nie wydrukuje tego rodzaju wyników, więc ta metoda nie jest odpowiednia


1

Miałem ten sam problem. Widziałem vi -b [filename]dodane znaki. Znalazłem znaki kontrolne ^@i ^M. Następnie w vi wpisz, :1,$s/^@//gaby usunąć ^@znaki. Powtórz to polecenie dla ^M.

Ostrzeżenie: aby uzyskać „niebieskie” znaki sterujące, naciśnij Ctrl+, va następnie Ctrl+ Mlub Ctrl+ @. Następnie zapisz i wyjdź vi.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.