Was lässt grep eine Datei als binär betrachten?


185

Ich habe einige Datenbank-Dumps von einem Windows-System auf meiner Box. Sie sind Textdateien. Ich benutze Cygwin, um sie zu durchsuchen. Dies scheinen reine Textdateien zu sein. Ich öffne sie mit Texteditoren wie Notepad und Wordpad und sie sehen gut lesbar aus. Wenn ich grep auf sie starte, wird es jedoch sagen binary file foo.txt matches.

Ich habe festgestellt, dass die Dateien einige ASCII- NULZeichen enthalten, die meines Erachtens Artefakte aus dem Datenbank-Dump sind.

Warum betrachtet grep diese Dateien als binär? Der NULCharakter? Gibt es eine Flagge im Dateisystem? Was muss ich ändern, damit grep mir die Zeilenübereinstimmungen anzeigt?


2
--null-datakann nützlich sein, wenn NULdas Trennzeichen ist.
Steve-o

Antworten:


125

Wenn NULsich irgendwo in der Datei ein Zeichen befindet, betrachtet grep es als Binärdatei.

Möglicherweise gibt es eine solche Problemumgehung cat file | tr -d '\000' | yourgrep, um zuerst alle Nullen zu entfernen und dann die Datei zu durchsuchen.


149
... oder benutze -a/ --text, zumindest mit GNU grep.
Derobert

1
@derobert: auf einigen (älteren) Systemen grep see lines, aber seine Ausgabe NULschneidet jede übereinstimmende Zeile zuerst ab (wahrscheinlich, weil es Cs printf aufruft und ihm die übereinstimmende Zeile gibt?). Auf einem solchen System gibt a grep cmd .sh_historyso viele leere Zeilen zurück, wie es Zeilen gibt, die mit 'cmd' übereinstimmen, da jede Zeile von sh_history ein bestimmtes Format mit einem NULam Anfang jeder Zeile hat. (Aber Ihr Kommentar "zumindest zu GNU grep" wird wahrscheinlich wahr. Ich habe momentan keinen zur Hand, um ihn zu testen, aber ich gehe davon aus, dass sie damit gut umgehen)
Olivier Dulac,

4
Ist das Vorhandensein eines NUL-Zeichens das einzige Kriterium? Das bezweifle ich. Es ist wahrscheinlich schlauer als das. Alles, was nicht in den Bereich von Ascii 32-126 fällt, würde ich vermuten, aber wir müssten uns den Quellcode ansehen, um sicherzugehen.
Michael Martinez

2
Meine Informationen stammten von der Manpage der jeweiligen grep-Instanz. Ihr Kommentar zur Implementierung ist gültig, Source Trumps Docs.
bbaja42

2
Ich hatte eine Datei, die grepunter Cygwin als binär eingestuft wurde, da sie einen langen Bindestrich (0x96) anstelle eines regulären ASCII-Bindestrichs / Minus (0x2d) enthielt. Ich denke, diese Antwort hat das Problem des OP gelöst, aber es scheint, dass es unvollständig ist.
cp.engr

121

grep -a arbeitete für mich:

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

4
Dies ist die beste, kostengünstigste Antwort IMO.
pydsigner

Aber nicht POSIX-konform
Matteo

21

Sie können die Verwendung stringsDienstprogramm , um den Textinhalt aus einer beliebigen Datei zu extrahieren und dann lenke sie durch grep, wie folgt aus : strings file | grep pattern.


2
Ideal zum Abrufen von Protokolldateien, die möglicherweise teilweise beschädigt sind
Hannes R.

ja, manchmal kommt es auch zu binären gemischten Logs. Das ist gut.
SDKKS

13

GNU grep 2.24 RTFS

Schlussfolgerung: Nur 2 und 2 Fälle:

  • NUL, z.B printf 'a\0' | grep 'a'

  • Codierungsfehler nach C99 mbrlen(), zB:

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

    da \x80kann nicht das erste Byte eines UTF-8-Unicode-Punkts sein: UTF-8 - Beschreibung | en.wikipedia.org

Darüber hinaus, wie von Stéphane Chazelas erwähnt. Warum betrachtet grep eine Datei als binär? | Unter Unix und Linux Stack Exchange werden diese Überprüfungen nur bis zum ersten Puffer mit der Länge TODO durchgeführt.

Nur bis zum ersten Puffer gelesen

Wenn also ein NUL- oder Codierungsfehler in der Mitte einer sehr großen Datei auftritt, wird diese möglicherweise trotzdem abgegriffen.

Ich stelle mir das aus Performancegründen vor.

ZB: Dies druckt die Zeile:

printf '%10000000s\n\x80a' | grep 'a'

aber das tut nicht:

printf '%10s\n\x80a' | grep 'a'

Die tatsächliche Puffergröße hängt davon ab, wie die Datei gelesen wird. ZB vergleichen:

export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'

Mit dem sleepwird die erste Zeile an grep übergeben, auch wenn sie nur 1 Byte lang ist, da der Prozess in den Ruhezustand wechselt und der zweite Lesevorgang nicht prüft, ob die Datei binär ist.

RTFS

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24

Finden Sie heraus, wo die stderr-Fehlermeldung codiert ist:

git grep 'Binary file'

Führt uns zu /src/grep.c:

if (!out_quiet && (encoding_error_output
                    || (0 <= nlines_first_null && nlines_first_null < nlines)))
    {
    printf (_("Binary file %s matches\n"), filename);

Wenn diese Variablen gut benannt wären, wären wir im Grunde genommen zu dem Schluss gekommen.

encoding_error_output

Das schnelle Greifen nach encoding_error_outputzeigt, dass der einzige Codepfad , der ihn ändern kann, durchläuft buf_has_encoding_errors:

clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
  return true;

dann eben man mbrlen.

nlines_first_null und nlines

Initialisiert als:

intmax_t nlines_first_null = -1;
nlines = 0;

Wenn also eine Null gefunden 0 <= nlines_first_nullwird , wird sie wahr.

TODO wann kann nlines_first_null < nlinesjemals falsch sein? Ich bin faul geworden.

POSIX

Definiert keine binären Optionen grep - Durchsucht eine Datei nach einem Muster | pubs.opengroup.org und GNU grep dokumentieren dies nicht, daher ist RTFS der einzige Weg.


1
Beeindruckende Erklärung!
user394

2
Beachten Sie, dass die Überprüfung auf gültiges UTF-8 nur in UTF-8-Gebietsschemas erfolgt. Beachten Sie auch, dass die Prüfung nur für den ersten Puffer durchgeführt wird, der aus der Datei gelesen wird, die für eine reguläre Datei 32768 Byte auf meinem System zu sein scheint, für eine Pipe oder einen Socket jedoch nur ein Byte groß sein kann. Vergleichen Sie (printf '\n\0y') | grep ymit (printf '\n'; sleep 1; printf '\0y') | grep yzum Beispiel.
Stéphane Chazelas

@ StéphaneChazelas "Beachten Sie, dass die Überprüfung auf gültiges UTF-8 nur in UTF-8-Gebietsschemata erfolgt": Meinst du das export LC_CTYPE='en_US.UTF-8'wie in meinem Beispiel oder etwas anderes? Buf las: erstaunliches Beispiel, hinzugefügt, um zu antworten. Sie haben offensichtlich die Quelle mehr als ich lesen, erinnert mich an jenem Hacker koans :-) „Der Student aufgeklärten war“
Ciro Santilli新疆改造中心法轮功六四事件

1
Ich habe auch nicht sehr detailliert nachgesehen, habe es aber kürzlich getan
Stéphane Chazelas,

1
@CiroSantilli 巴拿馬 巴拿馬 六四 事件 事件 Mit welcher Version von GNU grep haben Sie getestet?
jrw32982

6

Eine meiner Textdateien wurde plötzlich von grep als binär angesehen:

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

Lösung war, es zu konvertieren, indem man verwendet iconv:

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

1
Das ist mir auch passiert. Die Ursache war insbesondere ein ISO-8859-1-codierter nicht unterbrechender Speicherplatz, den ich durch einen normalen Speicherplatz ersetzen musste, damit grep in der Datei suchen konnte.
Gallaecio

4
grep 2.21 behandelt ISO-8859-Textdateien wie Binärdateien. Fügen Sie export LC_ALL = C vor dem Befehl grep hinzu.
Netawater

@netawater Danke! Dies ist zB der Fall, wenn Sie so etwas wie Müller in einer Textdatei haben. Das ist 0xFChexadezimal, also außerhalb des Bereichs, den grep für utf8 (bis zu 0x7F) erwarten würde . Prüfen Sie mit printf 'a \ x7F' | grep 'a' wie Ciro oben beschreibt.
Anne van Rossum

5

Die Datei /etc/magicoder /usr/share/misc/magichat eine Liste von Sequenzen, die der Befehl filezur Bestimmung des Dateityps verwendet.

Beachten Sie, dass die Binärdatei möglicherweise nur eine Ersatzlösung ist. Manchmal werden auch Dateien mit ungewöhnlicher Codierung als binär betrachtet.

grepUnter Linux gibt es einige Optionen, um Binärdateien wie --binary-filesoder zu verarbeiten-U / --binary


Genauer gesagt, Codierungsfehler nach C99 mbrlen(). Beispiel und Quelleninterpretation unter: unix.stackexchange.com/a/276028/32558
Ciro Santilli am

2

Einer meiner Schüler hatte dieses Problem. Es ist ein Fehler grepin Cygwin. Wenn die Datei nicht-ASCII - Zeichen hat, grepund egrepes als binäres sehen.


Das klingt nach einem Feature, nicht nach einem Bug. Insbesondere gibt es eine Befehlszeilenoption zur Steuerung (-a / --text)
Will Sheppard

2

Wenn Sie die Frage "Warum betrachtet grep eine Datei als binär?" Beantworten, können Sie Folgendes verwenden iconv:

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

In meinem Fall gab es spanische Zeichen, die in Texteditoren korrekt angezeigt wurden, aber von grep als binär betrachtet wurden. iconvDie Ausgabe wies mich auf die Zeilen- und Spaltennummern dieser Zeichen hin

Im Fall von NULZeichen iconvwerden diese als normal betrachtet und nicht gedruckt, sodass diese Methode nicht geeignet ist


1

Ich hatte das gleiche problem Früher vi -b [filename]habe ich die hinzugefügten Zeichen gesehen. Ich habe die Steuerzeichen ^@und gefunden ^M. Geben Sie dann in vi ein :1,$s/^@//g, um die ^@Zeichen zu entfernen . Wiederholen Sie diesen Befehl für ^M.

Warnung: Um die "blauen" Steuerzeichen zu erhalten, drücken Sie Ctrl+ und vdann Ctrl+ Moder Ctrl+ @. Dann speichern und beenden Sie vi.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.