Cosa rende grep un file binario?


185

Ho alcuni dump di database da un sistema Windows nella mia scatola. Sono file di testo. Sto usando Cygwin per superarli. Questi sembrano essere semplici file di testo; Li apro con editor di testo come blocco note e wordpad e sembrano leggibili. Tuttavia, quando avrò grep su di loro, dirà binary file foo.txt matches.

Ho notato che i file contengono alcuni NULcaratteri ASCII , che credo siano artefatti dal dump del database.

Quindi cosa rende grep considerare questi file come binari? Il NULpersonaggio? C'è un flag sul filesystem? Cosa devo cambiare per ottenere grep per mostrarmi le corrispondenze di linea?


2
--null-datapuò essere utile se NULè il delimitatore.
Steve-o

Risposte:


126

Se c'è un NULcarattere in qualsiasi parte del file, grep lo considererà come un file binario.

Potrebbe esserci una soluzione come questa cat file | tr -d '\000' | yourgrepper eliminare prima tutto il null, quindi per cercare nel file.


149
... oppure usa -a/ --text, almeno con GNU grep.
derobert,

1
@derobert: in realtà, su alcuni sistemi (più vecchi), grep vede le righe, ma il suo output NULtroncerà ogni riga corrispondente all'inizio (probabilmente perché chiama C printf e le dà la linea abbinata?). Su un tale sistema a grep cmd .sh_historyrestituirà tante righe vuote quante sono le righe corrispondenti a 'cmd', poiché ogni riga di sh_history ha un formato specifico con NULa all'inizio di ogni riga. (ma il tuo commento "almeno su GNU grep" probabilmente si avvera. Non ne ho uno a portata di mano adesso per testarlo, ma mi aspetto che lo gestiscano bene)
Olivier Dulac il

4
La presenza di un carattere NUL è l'unico criterio? Ne dubito. Probabilmente è più intelligente di così. Qualunque cosa che non rientri nell'intervallo Ascii 32-126 sarebbe la mia ipotesi, ma dovremmo guardare il codice sorgente per essere sicuri.
Michael Martinez,

2
Le mie informazioni provenivano dalla pagina man dell'istanza grep specifica. Il tuo commento sull'implementazione è valido, la fonte supera i documenti.
bbaja42,

2
Avevo un file che grepsu cygwin considerava binario perché aveva un trattino lungo (0x96) invece di un trattino / segno ASCII normale (0x2d). Immagino che questa risposta abbia risolto il problema del PO, ma sembra che sia incompleta.
cp.engr,

121

grep -a ha funzionato per me:

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

4
Questa è la risposta IMO migliore e meno costosa.
pydsigner,

Ma non conforme a POSIX
Matteo

21

È possibile utilizzare l' stringsutility per estrarre il contenuto del testo da qualsiasi file e quindi il tubo attraverso grep, in questo modo: strings file | grep pattern.


2
Ideale per grepping file di registro che potrebbero essere parzialmente danneggiati
Hannes R.,

sì, a volte succede anche la registrazione mista binaria. Questo è buono.
sdkks,

13

GNU grep 2.24 RTFS

Conclusione: solo 2 e 2 casi:

  • NUL, per esempio printf 'a\0' | grep 'a'

  • errore di codifica secondo C99 mbrlen(), ad es .:

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

    perché \x80non può essere il primo byte di un punto Unicode UTF-8 : UTF-8 - Descrizione | en.wikipedia.org

Inoltre, come indicato da Stéphane Chazelas Cosa rende grep un file binario? | Unix & Linux Stack Exchange , questi controlli vengono eseguiti solo fino alla prima lettura del buffer di lunghezza TODO.

Solo fino al primo buffer letto

Quindi, se si verifica un errore NUL o di codifica nel mezzo di un file di dimensioni molto grandi, potrebbe comunque essere grepped.

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 dalla modalità di lettura del file. Ad esempio confrontare:

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 è lunga solo 1 byte 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 fossero ben denominate, in pratica arriveremmo alla conclusione.

encoding_error_output

Quick grepping per encoding_error_outputmostra che l'unico 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;

quindi quando viene trovato un null 0 <= nlines_first_nulldiventa vero.

TODO quando nlines_first_null < nlinesmai può essere falso? Sono diventato pigro.

POSIX

Non definisce le opzioni binarie grep - cerca un modello per un modello | pubs.opengroup.org e GNU grep non lo documenta, quindi RTFS è l'unico modo.


1
Impressionante spiegazione!
user394

2
Si noti che il controllo per UTF-8 valido si verifica solo in 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 ycon (printf '\n'; sleep 1; printf '\0y') | grep yper esempio.
Stéphane Chazelas,

@ StéphaneChazelas "Notare che il controllo per UTF-8 valido si verifica solo in locali UTF-8": intendi export LC_CTYPE='en_US.UTF-8'come nel mio esempio o qualcos'altro? Buf read: fantastico esempio, aggiunto per rispondere. Hai ovviamente letto la fonte più di me, mi ricorda quei koan hacker "Lo studente è stato illuminato" :-)
Ciro Santilli 13 改造 中心 法轮功 六四 事件

1
Nemmeno io ho guardato nei minimi dettagli, ma molto recentemente
Stéphane Chazelas il

1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功 con quale versione di GNU grep hai provato?
jrw32982,

6

Uno dei miei file di testo è stato improvvisamente visto come binario da grep:

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

La soluzione era di convertirlo usando iconv:

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

1
Questo è successo anche a me. In particolare, la causa era uno spazio non interrotto con codifica ISO-8859-1, che dovevo sostituire con uno spazio normale per fare in modo che grep cercasse nel file.
Gallaecio,

4
grep 2.21 tratta i file di testo ISO-8859 come se fossero binari, aggiungi export LC_ALL = C prima del comando grep.
netawater,

@netawater Grazie! Questo è il caso, ad esempio, se hai qualcosa come Müller in un file di testo. È 0xFCesadecimale, quindi al di fuori dell'intervallo che grep si aspetterebbe per utf8 (fino a 0x7F). Verificare con printf 'a \ x7F' | grep 'a' come Ciro descrive sopra.
Anne van Rossum,

5

Il file /etc/magico /usr/share/misc/magiccontiene un elenco di sequenze utilizzate dal comando fileper determinare il tipo di file.

Si noti che il binario potrebbe essere solo una soluzione di fallback. A volte anche i file con strana codifica sono considerati binari.

grepsu Linux ha alcune opzioni per gestire file binari come --binary-fileso-U / --binary


Più precisamente, errore di codifica secondo C99 mbrlen(). Esempio e interpretazione della fonte su: unix.stackexchange.com/a/276028/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

2

Uno dei miei studenti ha avuto questo problema. C'è un bug grepin Cygwin. Se il file ha caratteri non Ascii grepe egrepvederlo come binario.


Sembra una caratteristica, non un bug. Soprattutto dato che esiste un'opzione da riga di comando per controllarlo (-a / --text)
Will Sheppard

2

In realtà rispondendo alla domanda "Cosa rende grep un file binario?", Puoi usare iconv:

$ iconv < myfile.java
iconv: (stdin):267:70: cannot convert

Nel mio caso c'erano personaggi spagnoli che venivano visualizzati correttamente negli editor di testo ma grep li considerava binari; iconvl'output mi ha indicato i numeri di riga e colonna di quei caratteri

Nel caso dei NULcaratteri, iconvli considererà normali e non stamperanno quel tipo di output, quindi questo metodo non è adatto


1

Ho avuto lo stesso problema. Ho usato vi -b [filename]per vedere i caratteri aggiunti. Ho trovato i personaggi di controllo ^@e ^M. Quindi in vi digitare :1,$s/^@//gper rimuovere i ^@caratteri. Ripeti questo comando per ^M.

Avvertenza: per ottenere i caratteri di controllo "blu" premere Ctrl+, vquindi Ctrl+ Mo Ctrl+ @. Quindi salvare ed uscire da vi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.