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
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 systemiegrep 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 zNUL
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
, npprintf "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 (do0x7F
). 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
- Dokładniej, błąd kodowania zgodny z C99 ' s
mbrlen()
. Przykład i interpretacja źródła pod adresem: unix.stackexchange.com/a/276028/32558
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.
--null-data
mogą być przydatne, jeśliNUL
to separator.