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

  • --null-data poate fi util dacă NUL este delimitatorul.

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, un grep 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 un NUL 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 ex printf "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”>

🙂

  • Nici ‘ nu am analizat detaliile, dar a făcut foarte recent
  • @CiroSantilli 巴拿馬 文件 六四 事件 法轮功 cu ce versiune de GNU grep ați testat?
  • 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ă la 0x7F). 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

    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.

    Lasă un răspuns

    Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *