Mam na komputerze zrzuty bazy danych z systemu Windows. Są to pliki tekstowe. Używam cygwin do przeglądania ich. Wydają się być zwykłymi plikami tekstowymi. Otwieram je za pomocą edytorów tekstu, takich jak notatnik i wordpad, i wyglądają na czytelne. Jednak gdy uruchomię na nich grep, wyświetli się binary file foo.txt matches.

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

Więc co sprawia, że grep uważa te pliki za binarne? Znak NUL? Czy w systemie plików jest flaga? Co muszę zmienić, aby grep miał pokaż mi pasującą linię?

Komentarze

  • --null-data mogą być przydatne, jeśli NUL to separator.

Odpowiedź

Jeśli występuje NUL znak w dowolnym miejscu pliku, grep potraktuje go jako plik binarny.

Można zastosować obejście takie jak cat file | tr -d "\000" | yourgrep, aby wyeliminować najpierw wszystkie null i następnie przeszukać plik.

Komentarze

  • … lub użyj -a / --text, przynajmniej z GNU grep.
  • @derobert: w rzeczywistości w niektórych (starszych) systemach grep see wiersze, ale jego wyjście obetnie każdy pasujący wiersz na początku NUL (prawdopodobnie dlatego, że wywołuje C ' s printf i podaje dopasowaną linię?). W takim systemie grep cmd .sh_history zwróci tyle pustych wierszy, ile jest wierszy pasujących do ' cmd ', ponieważ każda linia sh_history ma określony format z NUL na początku każdego wiersza. (ale Twój komentarz ” przynajmniej na temat GNU grep ” prawdopodobnie się spełni. Nie ' nie mam teraz jednego pod ręką do przetestowania, ale spodziewam się, że dobrze sobie z tym poradzą)
  • Czy obecność znaku NUL jest jedynym kryterium? Wątpię. To ' jest prawdopodobnie mądrzejsze. Wszystko wykraczające poza zakres Ascii 32-126 to moje przypuszczenie, ale ' musielibyśmy sprawdzić kod źródłowy, aby się upewnić.
  • Moje informacje ze strony podręcznika man konkretnej instancji grep. Twój komentarz dotyczący implementacji jest prawidłowy, źródło przewyższa dokumenty.
  • Miałem plik, który grep na cygwin był uważany za binarny, ponieważ miał długi myślnik (0x96) zamiast zwykły łącznik / minus ASCII (0x2d). Myślę, że ta odpowiedź rozwiązała problem OP ', ale wygląda na to, że jest niekompletna.

Odpowiedź

grep -a pracował dla mnie:

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

Komentarze

  • To jest najlepsza i najtańsza odpowiedź IMO.
  • Ale niezgodna z POSIX
  • Czy mógłbyś wyjaśnić, dlaczego tak nie jest? Dobrze byłoby to wyjaśnić wszystkim z nas, którzy uważają tę odpowiedź za opcję. Dzięki :).
  • Hej, ' przyszedłem tu DRUGI raz, aby ponownie nauczyć się tego LOL. Francuski akcent (znak diakrytyczny) w tekście powodował, że grep zmieniał się

Odpowiedź

Możesz użyć strings narzędzie do wyodrębnienia treści tekstowej z dowolnego pliku, a następnie potokiem go przez grep, na przykład: strings file | grep pattern.

Komentarze

  • Idealne do grepowania plików dziennika, które mogą być częściowo uszkodzone
  • tak, czasami binarne logowanie mieszane też się dzieje. To dobrze.

Odpowiedź

GNU grep 2.24 RTFS

Wniosek: tylko 2 i 2 przypadki:

  • NUL, np printf "a\0" | grep "a"

  • błąd kodowania według C99 mbrlen(), np .:

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

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

Ponadto, jak wspomniał Stéphane Chazelas Co sprawia, że grep uważa plik za być binarnym? | Unix & Linux Stack Exchange , te sprawdzenia są wykonywane tylko do pierwszego odczytu bufora o długości TODO.

Tylko do pierwszego odczytu bufora

Więc jeśli błąd NUL lub kodowania wystąpi w środku bardzo dużego pliku, może i tak być grepowane.

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

Na przykład: to wypisuje wiersz:

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

ale tak nie jest:

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

Rzeczywisty rozmiar bufora zależy od sposobu odczytu pliku. Na przykład.porównaj:

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

W przypadku sleep pierwsza linia jest przekazywana do grepa, nawet jeśli ma tylko 1 bajt długi, ponieważ proces przechodzi w stan 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, w zasadzie doszliśmy do wniosku.

encoding_error_output

Szybkie grepowanie dla encoding_error_output pokazuje, że jedyna ścieżka kodu, którą można zmodyfikować, prowadzi przez buf_has_encoding_errors:

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

, a następnie tylko man mbrlen.

nlines_first_null i nlines

Zainicjowane jako:

intmax_t nlines_first_null = -1; nlines = 0; 

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

DO ZROBIENIA, kiedy można nlines_first_null < nlines kiedykolwiek będzie fałszywe? Zrobiłem się leniwy.

POSIX

Nie definiuje opcji binarnych grep – szukaj w pliku wzorca | pubs.opengroup.org , a GNU grep tego nie dokumentuje, więc RTFS jest jedynym sposobem.

Komentarze

  • Imponujące wyjaśnienie !
  • Zauważ, że sprawdzenie poprawności UTF-8 ma miejsce tylko w ustawieniach regionalnych UTF-8. Zwróć również uwagę, że sprawdzenie jest wykonywane 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 to być zaledwie jeden bajt. Porównaj na przykład (printf '\n\0y') | grep y z (printf '\n'; sleep 1; printf '\0y') | grep y.
  • @St é phaneChazelas ” Zwróć uwagę, że sprawdzenie poprawności UTF-8 odbywa się tylko w językach UTF-8 „: czy masz na myśli export LC_CTYPE='en_US.UTF-8' jak w moim przykładzie, czy coś innego? Buf przeczytaj: niesamowity przykład, dodany do odpowiedzi. Oczywiście częściej czytałeś źródło niż ja, przypomina mi te koany hakerskie ” Student był oświecony ” 🙂
  • Nie ' też nie zagłębiłem się w szczegóły, ale ostatnio
  • @CiroSantilli 巴拿馬 文件 六四 事件 法轮功 z jaką wersją GNU grep testowałeś?

Odpowiedź

Grep nagle zobaczył jeden z moich plików tekstowych jako binarny:

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

Rozwiązaniem była konwersja za pomocą iconv:

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

Komentarze

  • Mnie też się to przydarzyło. W szczególności przyczyną była nierozerwalna spacja zakodowana w ISO-8859-1, którą musiałem zastąpić zwykłą spacją, aby grep przeszukiwał plik.
  • grep 2.21 traktuje ISO -8859 plików tekstowych tak, jakby były binarne, dodaj eksport LC_ALL = C przed poleceniem grep.
  • @netawater Dzięki! To jest np. przypadek, jeśli masz coś takiego jak M ü ller w pliku tekstowym. To ' s 0xFC szesnastkowe, więc poza zakresem grep oczekiwałby dla utf8 (do 0x7F). Sprawdź z printf ' a \ x7F ' | grep ' a ' jak opisał Ciro powyżej.

Odpowiedź

Plik /etc/magic lub /usr/share/misc/magic zawiera listę sekwencji, których polecenie file używa do określenia typu pliku.

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

grep w systemie Linux ma kilka opcji obsługi plików binarnych, takich jak --binary-files lub -U / --binary

Komentarze

Answer

Jeden z moich uczniów miał ten problem. Wystąpił błąd w grep w Cygwin. Jeśli plik zawiera znaki spoza zestawu ASCII, grep i egrep widzą go jako binarny.

Komentarze

  • To brzmi jak funkcja, a nie błąd.Szczególnie biorąc pod uwagę, że istnieje opcja wiersza poleceń do kontrolowania tego (-a / –text)

Odpowiedź

Właściwie odpowiadając na pytanie „Co sprawia, że grep uważa plik za 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 pojawiały się poprawnie w edytorach tekstu, ale grep uważał je za binarne; iconv dane wyjściowe wskazały numery wierszy i kolumn tych znaków

W przypadku NUL znaków, iconv uzna je za normalne i nie wydrukuje tego rodzaju danych wyjściowych, więc ta metoda jest nieodpowiednia

Odpowiedź

Miałem ten sam problem. Użyłem vi -b [filename], aby zobaczyć dodane znaki. Znalazłem znaki sterujące ^@ i ^M. Następnie w vi wpisz :1,$s/^@//g, aby usunąć znaki ^@. Powtórz to polecenie dla ^M.

Ostrzeżenie: aby uzyskać „niebieskie” znaki sterujące, naciśnij klawisze Ctrl + v , a następnie Ctrl + M lub Ctrl + @ . Następnie zapisz i wyjdź z vi.

Odpowiedź

Też miałem ten problem, ale w moim przypadku był on spowodowany, gdy dopasowana linia to zbyt długi.

file myfile.txt myfile.txt: UTF-8 Unicode text, with very long lines 

grep będzie przebiegał przez cały plik z wieloma wzorami, ale gdy wzorzec pasował do ” bardzo długa linia ” kończy się na Binary file myfile.txt matches.

Dodanie -a również rozwiązuje ten problem, ale wstępne przeanalizowanie pliku pod kątem NULL lub innych nieprawidłowych znaków nie przyniosłoby żadnego efektu (w przeciwnym razie grep nie byłby kompletny dla innych wzorców). W tym przypadku naruszająca linia miała ponad 25 tys. Znaków!

Nie rozumiem, dlaczego dzieje się tak tylko wtedy, gdy grep próbuje zwrócić wiersz, a nie kiedy przetwarza go w poszukiwaniu innych wzorców.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *