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.