Am câteva cutii de date dintr-un sistem Windows în cutia mea. Sunt fișiere text. Folosesc cygwin pentru a le grepă. Acestea par fi fișiere text simple; le deschid cu editori de text precum blocnotes și wordpad și arată lizibil. Cu toate acestea, când rulez grep pe ele, se va spune binary file foo.txt matches
.
Am observat că fișierele conțin câteva caractere ascii NUL
, care cred că sunt artefacte din dump-ul bazei de date.
Deci, ce face grep să considere aceste fișiere binare? Caracterul NUL
? Există un steag în sistemul de fișiere? Ce trebuie să schimb pentru a obține grep la arată-mi potrivirile de linie?
Comentarii
Răspuns
Dacă există un NUL
oriunde în fișier, grep îl va considera ca un fișier binar.
S-ar putea să existe o soluție ca aceasta cat file | tr -d "\000" | yourgrep
pentru a elimina toate nule mai întâi și apoi pentru a căuta prin fișier.
Comentarii
- … sau utilizați
-a
/--text
, cel puțin cu GNU grep. - @derobert: de fapt, pe unele sisteme (mai vechi), grep vede linii, dar ieșirea sa va trunchia fiecare linie potrivită la prima
NUL
(probabil pentru că apelează C ‘ s printf și îi dă linia potrivită?). Pe un astfel de sistem, ungrep cmd .sh_history
va returna câte linii goale câte linii corespund ‘ cmd ‘, deoarece fiecare linie din sh_history are un format specific cu unNUL
la începutul fiecărei linii. (dar comentariul tău ” cel puțin pe GNU grep ” devine probabil adevărat. Nu ‘ Nu am unul la îndemână acum pentru a testa, dar mă aștept să se descurce frumos) - Prezența unui caracter NUL este singurul criteriu? Mă îndoiesc de asta. Este ‘ probabil mai inteligent decât atât. Orice ar cădea în afara intervalului Ascii 32-126 ar fi presupunerea mea, dar ‘ ar trebui să ne uităm la codul sursă pentru a fi siguri.
- Informațiile mele au fost din pagina manuală a instanței grep specifice. Comentariul dvs. despre implementare este valid, sursa depășește documentele.
- Aveam un fișier pe care
grep
îl considera cygwin binar deoarece avea o liniuță lungă (0x96) în loc de o cratimă ASCII obișnuită / minus (0x2d). Cred că acest răspuns a rezolvat problema OP ‘, dar se pare că este incompletă.
Răspuns
grep -a
a funcționat pentru mine:
$ grep --help [...] -a, --text equivalent to --binary-files=text
Comentarii
- Acesta este cel mai bun și mai puțin costisitor răspuns IMO.
- Dar nu este compatibil POSIX
- V-ar deranja să explicați de ce nu este? Ar fi bine să clarificăm, pentru toți cei care găsim acest răspuns ca opțiune. Mulțumesc :).
- Bună, ‘ am venit aici a doua oară pentru a învăța din nou acest LOL. Un accent francez (diacritic) din text a cauzat grep la barf
Răspuns
Puteți utiliza strings
pentru a extrage conținutul textului din orice fișier și apoi îl introduceți prin grep
, astfel: strings file | grep pattern
.
Comentarii
- Ideal pentru grepping fișiere jurnal care ar putea fi parțial corupte
- da, uneori jurnal mixt binar se întâmplă, de asemenea. Aceasta este bună.
Răspuns
GNU grep 2.24 RTFS
Concluzie: numai 2 și 2 cazuri:
-
NUL
, de exprintf "a\0" | grep "a"
-
eroare de codificare conform C99
mbrlen()
, de exemplu:export LC_CTYPE="en_US.UTF-8" printf "a\x80" | grep "a"
deoarece
\x80
nu poate fi primul octet al unui punct Unicode UTF-8: UTF-8 – Descriere | en.wikipedia.org
Mai mult, așa cum a menționat Stéphane Chazelas Ce face grep să considere un fișier pentru fi binar? | Unix & Linux Stack Exchange , aceste verificări se fac doar până la prima citire a bufferului de lungime TODO.
Numai până la primul buffer citit
Deci, dacă apare o eroare NUL sau de codificare în mijlocul unui fișier foarte mare, s-ar putea fi oricum oricum.
Îmi imaginez că acest lucru este din motive de performanță.
De exemplu: acesta imprimă linia:
printf "%10000000s\n\x80a" | grep "a"
dar acest lucru nu:
printf "%10s\n\x80a" | grep "a"
Dimensiunea reală a bufferului depinde de modul în care este citit fișierul. De exemplu.compare:
export LC_CTYPE="en_US.UTF-8" (printf "\n\x80a") | grep "a" (printf "\n"; sleep 1; printf "\x80a") | grep "a"
Cu sleep
, prima linie este trecută la grep chiar dacă este doar 1 octet lung, deoarece procesul se oprește, iar a doua citire nu verifică dacă fișierul este binar.
RTFS
git clone git://git.savannah.gnu.org/grep.git cd grep git checkout v2.24
Găsiți unde este codat mesajul de eroare stderr:
git grep "Binary file"
Ne conduce la /src/grep.c
:
if (!out_quiet && (encoding_error_output || (0 <= nlines_first_null && nlines_first_null < nlines))) { printf (_("Binary file %s matches\n"), filename);
Dacă acele variabile au fost bine denumite, am ajuns la concluzie.
encoding_error_output
Grepping rapid pentru encoding_error_output
arată că singura cale de cod care o poate modifica trece prin buf_has_encoding_errors
:
clen = mbrlen (p, buf + size - p, &mbs); if ((size_t) -2 <= clen) return true;
apoi doar man mbrlen
.
nlines_first_null și nlines
Initializat ca:
intmax_t nlines_first_null = -1; nlines = 0;
deci atunci când se găsește un nul 0 <= nlines_first_null
devine adevărat.
TODO când poate nlines_first_null < nlines
a fost vreodată fals? M-am leneș.
POSIX
Nu definește opțiunile binare grep – căutați într-un fișier un model | pubs.opengroup.org , iar GNU grep nu îl documentează, așa că RTFS este singura cale.
Comentarii
- Explicație impresionantă !
- Rețineți că verificarea UTF-8 validă se întâmplă numai în regiunile UTF-8. De asemenea, rețineți că verificarea se face numai pe primul buffer citit din fișier, care pentru un fișier obișnuit pare a fi 32768 octeți în sistemul meu, dar pentru o conductă sau o priză poate fi la fel de mic ca un octet. Comparați
(printf '\n\0y') | grep y
cu(printf '\n'; sleep 1; printf '\0y') | grep y
de exemplu. - @St é phaneChazelas ” Rețineți că verificarea UTF-8 validă are loc numai în regiunile UTF-8 „: vrei să spui despre
export LC_CTYPE='en_US.UTF-8'
ca în exemplul meu sau altceva? Buf citit: exemplu uimitor, adăugat pentru a răspunde. Evident, ați citit sursa mai mult decât mine, îmi amintește de cei hacker koans ” Studentul a fost luminat id = „5346406a6c”>
🙂
Răspuns
Unul dintre fișierele mele text a fost brusc văzut ca binar de grep:
$ file foo.txt foo.txt: ISO-8859 text
Soluția a fost să o convertiți utilizând iconv
:
iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
Comentarii
- Acest lucru mi s-a întâmplat și mie. În special, cauza a fost un spațiu necodat codat ISO-8859-1, pe care a trebuit să îl înlocuiesc cu un spațiu obișnuit pentru a face grep să caute în fișier.
- grep 2.21 tratează ISO -8859 fișiere text ca și cum ar fi binare, adăugați export LC_ALL = C înainte de comanda grep.
- @netawater Mulțumesc! Aceasta este de ex. cazul dacă aveți ceva de genul M ü ller într-un fișier text. Acel ‘ s
0xFC
hexadecimal, deci în afara intervalului grep s-ar aștepta pentru utf8 (până la0x7F
). Verificați cu printf ‘ a \ x7F ‘ | grep ‘ a ‘ așa cum descrie Ciro mai sus.
Răspuns
Fișierul /etc/magic
sau /usr/share/misc/magic
are o listă de secvențe pe care comanda file
utilizează pentru determinarea tipului de fișier.
Rețineți că binarul poate fi doar o soluție alternativă. Uneori, fișierele cu codificare ciudată sunt considerate și binare.
grep
pe Linux are câteva opțiuni pentru a gestiona fișiere binare precum --binary-files
sau -U / --binary
Comentarii
- Mai precis, eroare de codificare conform C99 ‘ s
mbrlen()
. Exemplu și interpretare sursă la: unix.stackexchange.com/a/276028/32558
Răspuns
Unul dintre elevii mei a avut această problemă. Există o eroare în grep
în Cygwin
. Dacă fișierul are caractere care nu sunt Ascii, grep
și egrep
îl văd ca binar.
Comentarii
- Sună ca o caracteristică, nu ca o eroare.În special, există o opțiune de linie de comandă pentru a o controla (-a / –text)
Răspuns
Răspunzând de fapt la întrebarea „Ce face grep să considere un fișier ca fiind binar?”, Puteți utiliza iconv
:
$ iconv < myfile.java iconv: (stdin):267:70: cannot convert
În cazul meu, au existat caractere spaniole care au apărut corect în editorii de text, dar grep le-a considerat binare; iconv
ieșirea mi-a indicat numerele de linie și de coloană ale acelor caractere
În cazul NUL
caractere, iconv
le va considera normale și nu va imprima acel tip de ieșire, astfel încât această metodă nu este potrivită
Răspuns
Am avut aceeași problemă. Am folosit vi -b [filename]
pentru a vedea caracterele adăugate. Am găsit caracterele de control ^@
și ^M
. Apoi în vi tastați :1,$s/^@//g
pentru a elimina caracterele ^@
. Repetați această comandă pentru ^M
.
Avertisment: pentru a obține caracterele de control „albastre”, apăsați Ctrl + v apoi Ctrl + M sau Ctrl + @ . Apoi salvați și ieșiți din vi.
Răspuns
Am avut și eu această problemă, dar în cazul meu a fost cauzată când o linie potrivită este prea lung.
file myfile.txt myfile.txt: UTF-8 Unicode text, with very long lines
grep
ar rula întregul fișier bine cu multe modele, dar atunci când un model se potrivea cu un ” linie foarte lungă ” s-a oprit cu Binary file myfile.txt matches
.
Adăugarea -a
rezolvă, de asemenea, această problemă, dar pre-analizarea fișierului pentru NULL sau alte caractere nevalide nu ar avea niciun efect (nu există altele grep nu s-ar finaliza pentru alte tipare). În acest caz, linia ofensatoare avea 25k + caractere!
Ceea ce nu înțeleg este de ce se întâmplă numai atunci când grep
încearcă să returneze linia și nu când îl procesează căutând alte tipare.
--null-data
poate fi util dacăNUL
este delimitatorul.