Ho alcuni dump del database da un sistema Windows sulla mia macchina. Sono file di testo. Sto usando cygwin per eseguire grep. Questi sembrano essere file di testo semplice; li apro con editor di testo come blocco note e wordpad e sembrano leggibili. Tuttavia, quando eseguo grep su di essi, verrà visualizzato binary file foo.txt matches
.
Ho notato che i file contengono alcuni caratteri ascii NUL
, che credo siano artefatti dal dump del database.
Quindi cosa fa sì che grep consideri questi file come binari? Il carattere NUL
? Cè un flag nel filesystem? Cosa devo cambiare per ottenere grep mostrarmi le corrispondenze di riga?
Commenti
Risposta
Se è presente un NUL
in qualsiasi punto del file, grep lo considererà un file binario.
Potrebbe essere una soluzione alternativa come questa cat file | tr -d "\000" | yourgrep
per eliminare prima tutto nullo e quindi per cercare nel file.
Commenti
- … oppure usa
-a
/--text
, almeno con GNU grep. - @derobert: in realtà, su alcuni sistemi (meno recenti) grep vede le righe, ma il suo output troncerà ogni riga corrispondente alla prima
NUL
(probabilmente perché chiama C ‘ s printf e gli fornisce la riga corrispondente?). Su un tale sistema, ungrep cmd .sh_history
restituirà tante righe vuote quante sono le righe corrispondenti a ‘ cmd ‘, poiché ogni riga di sh_history ha un formato specifico con unNUL
allinizio di ogni riga. (ma il tuo commento ” almeno su GNU grep ” probabilmente si avvera. Io non ‘ ne ho uno a portata di mano in questo momento da testare, ma mi aspetto che lo gestiscano bene) - La presenza di un carattere NUL è lunico criterio? Ne dubito. ‘ è probabilmente più intelligente di così. Tutto ciò che non rientra nellintervallo Ascii 32-126 sarebbe la mia ipotesi, ma ‘ avremmo dovuto guardare il codice sorgente per essere sicuri.
- Le mie informazioni erano dalla pagina man della specifica istanza grep. Il tuo commento sullimplementazione è valido, la fonte supera i documenti.
- Avevo un file che
grep
su cygwin considerava binario perché aveva un trattino lungo (0x96) invece di un normale trattino ASCII / meno (0x2d). Immagino che questa risposta abbia risolto il problema dellOP ‘, ma sembra che sia incompleto.
Risposta
grep -a
ha funzionato per me:
$ grep --help [...] -a, --text equivalent to --binary-files=text
Commenti
- Questa è la risposta migliore e meno costosa IMO.
- Ma non conforme a POSIX
- Ti dispiacerebbe spiegare perché non lo è? Sarebbe bene chiarirlo, per tutti noi che troviamo questa risposta come unopzione. Grazie :).
- Ciao, ‘ sono venuto qui una SECONDA volta per imparare di nuovo questo LOL. Un accento francese (diacritico) nel testo faceva barf a grep
Answer
Puoi usare strings
utilità per estrarre il contenuto di testo da qualsiasi file e quindi reindirizzarlo tramite grep
, in questo modo: strings file | grep pattern
.
Commenti
- Ideale per grepping file di log che potrebbero essere parzialmente danneggiati
- sì, a volte logging binario misto succede anche. Questo va bene.
Risposta
GNU grep 2.24 RTFS
Conclusione: solo 2 e 2 casi:
-
NUL
, ad esprintf "a\0" | grep "a"
-
errore di codifica in base a C99
mbrlen()
, ad esempio:export LC_CTYPE="en_US.UTF-8" printf "a\x80" | grep "a"
perché
\x80
non può essere il primo byte di un punto Unicode UTF-8: UTF-8 – Descrizione | en.wikipedia.org
Inoltre, come menzionato da Stéphane Chazelas Ciò che fa considerare grep un file da essere binario? | Unix & Linux Stack Exchange , questi controlli vengono eseguiti solo fino alla prima lettura del buffer di lunghezza TODO.
Solo fino al primo buffer di lettura
Quindi, se si verifica un errore NUL o di codifica nel mezzo di un file molto grande, potrebbe essere grepped comunque.
Immagino che questo sia per motivi di prestazioni.
Ad esempio: questo stampa la riga:
printf "%10000000s\n\x80a" | grep "a"
ma questo non:
printf "%10s\n\x80a" | grep "a"
La dimensione effettiva del buffer dipende da come viene letto il file. Per esempio.confronta:
export LC_CTYPE="en_US.UTF-8" (printf "\n\x80a") | grep "a" (printf "\n"; sleep 1; printf "\x80a") | grep "a"
Con sleep
, la prima riga viene passata a grep anche se è solo 1 byte lungo perché il processo va in sospensione e la seconda lettura non controlla se il file è binario.
RTFS
git clone git://git.savannah.gnu.org/grep.git cd grep git checkout v2.24
Trova dove è codificato il messaggio di errore stderr:
git grep "Binary file"
Ci porta a /src/grep.c
:
if (!out_quiet && (encoding_error_output || (0 <= nlines_first_null && nlines_first_null < nlines))) { printf (_("Binary file %s matches\n"), filename);
Se quelle variabili erano ben denominate, in pratica siamo giunti alla conclusione.
encoding_error_output
Grepping rapido per encoding_error_output
mostra che lunico percorso di codice che può modificarlo passa attraverso buf_has_encoding_errors
:
clen = mbrlen (p, buf + size - p, &mbs); if ((size_t) -2 <= clen) return true;
quindi solo man mbrlen
.
nlines_first_null e nlines
Inizializzato come:
intmax_t nlines_first_null = -1; nlines = 0;
così quando viene trovato un valore nullo 0 <= nlines_first_null
diventa vero.
TODO quando è possibile nlines_first_null < nlines
mai essere falso? Sono diventato pigro.
POSIX
Non definisce le opzioni binarie grep – cerca un pattern | pubs.opengroup.org e GNU grep non lo documenta, quindi RTFS è lunico modo.
Commenti
- Spiegazione impressionante !
- Nota che il controllo per UTF-8 valido avviene solo nelle versioni locali UTF-8. Si noti inoltre che il controllo viene eseguito solo sul primo buffer letto dal file che per un file normale sembra essere 32768 byte sul mio sistema, ma per una pipe o un socket può essere piccolo come un byte. Confronta
(printf '\n\0y') | grep y
con(printf '\n'; sleep 1; printf '\0y') | grep y
ad esempio. - @St é phaneChazelas ” Tieni presente che il controllo di UTF-8 valido avviene solo nelle versioni locali UTF-8 “: intendi per
export LC_CTYPE='en_US.UTF-8'
come nel mio esempio o qualcosaltro? Buf letto: esempio straordinario, aggiunto alla risposta. Ovviamente hai letto la fonte più di me, mi ricorda quei koan degli hacker ” Lo studente era illuminato ” 🙂 - ‘ non ho nemmeno esaminato i dettagli, ma ha fatto molto di recente
- @CiroSantilli 巴拿馬 文件 六四 事件 法轮功 con quale versione di GNU grep hai testato?
Risposta
Uno dei miei file di testo è stato improvvisamente visto come binario da grep:
$ file foo.txt foo.txt: ISO-8859 text
La soluzione era convertirlo utilizzando iconv
:
iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
Commenti
- È successo anche a me. In particolare, la causa era uno spazio unificatore codificato ISO-8859-1, che ho dovuto sostituire con uno spazio normale per fare in modo che grep cercasse nel file.
- grep 2.21 tratta ISO -8859 file di testo come se fossero binari, aggiungi export LC_ALL = C prima del comando grep.
- @netawater Grazie! Questo è ad es. il caso se hai qualcosa come M ü ller in un file di testo. Questo ‘ è
0xFC
esadecimale, quindi al di fuori dellintervallo che grep si aspetta per utf8 (fino a0x7F
). Verifica con printf ‘ a \ x7F ‘ | grep ‘ a ‘ come Ciro ha descritto sopra.
Risposta
Il file /etc/magic
o /usr/share/misc/magic
ha un elenco di sequenze che il comando file
utilizza per determinare il tipo di file.
Nota che il binario potrebbe essere solo una soluzione di riserva. A volte anche i file con una codifica strana sono considerati binari.
grep
su Linux ha alcune opzioni per gestire i file binari come --binary-files
o -U / --binary
Commenti
- Più precisamente, errore di codifica secondo C99 ‘ s
mbrlen()
. Esempio e interpretazione della fonte su: unix.stackexchange.com/a/276028/32558
Risposta
Uno dei miei studenti ha riscontrato questo problema. È presente un bug in grep
in Cygwin
. Se il file contiene caratteri non Ascii, grep
e egrep
lo vedono come binario.
Commenti
- Sembra una funzionalità, non un bug.Soprattutto dato che esiste unopzione della riga di comando per controllarlo (-a / –text)
Answer
In realtà rispondendo alla domanda “Cosa fa considerare grep un file binario?”, Puoi utilizzare iconv
:
$ iconv < myfile.java iconv: (stdin):267:70: cannot convert
Nel mio caso cerano caratteri spagnoli che venivano visualizzati correttamente negli editor di testo ma grep li considerava binari; iconv
mi ha indicato i numeri di riga e di colonna di quei caratteri
Nel caso di NUL
caratteri, iconv
li considererà normali e non stamperà quel tipo di output quindi questo metodo non è adatto
Risposta
Ho avuto lo stesso problema. Ho utilizzato vi -b [filename]
per vedere i caratteri aggiunti. Ho trovato i caratteri di controllo ^@
e ^M
. Quindi in vi digita :1,$s/^@//g
per rimuovere i caratteri ^@
. Ripeti questo comando per ^M
.
Avviso: per ottenere i caratteri di controllo “blu”, premere Ctrl + v quindi Ctrl + M o Ctrl + @ . Quindi salva ed esci da vi.
Risposta
Anchio ho avuto questo problema ma nel mio caso è stato causato da una riga corrispondente troppo lungo.
file myfile.txt myfile.txt: UTF-8 Unicode text, with very long lines
grep
eseguiva lintero file senza problemi con molti pattern, ma quando un pattern corrisponde a un ” riga molto lunga ” si è interrotta con Binary file myfile.txt matches
.
Anche laggiunta di -a
risolve questo problema, ma la pre-analisi del file per NULL o altri caratteri non validi non avrebbe alcun effetto (non ce ne sono, altrimenti grep non sarebbe completo per altri pattern). In questo caso la riga incriminata aveva più di 25.000 caratteri!
Quello che non capisco è perché accade solo quando grep
cerca di restituire la riga e non quando lo sta elaborando cercando altri modelli.
--null-data
può essere utile seNUL
è il delimitatore.