Ik heb een aantal database-dumps van een Windows-systeem op mijn box. Het zijn tekstbestanden. Ik gebruik cygwin om er doorheen te grepen. Dit lijken platte tekstbestanden te zijn; ik open ze met teksteditors zoals kladblok en wordpad en ze zien er leesbaar uit. Als ik er echter grep op draai, staat er binary file foo.txt matches
.
Ik heb gemerkt dat de bestanden enkele ascii NUL
-tekens bevatten, die naar mijn mening artefacten zijn uit de database-dump.
Dus waarom beschouwt grep deze bestanden als binair? Het NUL
karakter? Staat er een vlag op het bestandssysteem? Wat moet ik veranderen om grep naar laat me de regelovereenkomsten zien?
Reacties
Antwoord
Als er een NUL
teken ergens in het bestand, grep beschouwt het als een binair bestand.
Er kan een oplossing als deze cat file | tr -d "\000" | yourgrep
voorkomen alle nul eerst, en en vervolgens door het bestand te zoeken.
Opmerkingen
- … of gebruik
-a
/--text
, in ieder geval met GNU grep. - @derobert: eigenlijk, op sommige (oudere) systemen, ziet grep regels, maar de uitvoer zal elke overeenkomende regel bij de eerste afkappen
NUL
(waarschijnlijk omdat het C ‘ s printf aanroept en het de overeenkomende regel geeft?). Op zon systeem zal eengrep cmd .sh_history
net zoveel lege regels retourneren als er regels zijn die overeenkomen met ‘ cmd ‘, aangezien elke regel van sh_history een specifiek formaat heeft met eenNUL
aan het begin van elke regel. (maar je opmerking ” in ieder geval op GNU grep ” komt waarschijnlijk uit. Ik wil niet ‘ ik heb er momenteel geen om te testen, maar ik verwacht dat ze dit netjes afhandelen) - Is de aanwezigheid van een NUL-teken het enige criterium? Ik betwijfel het. Het ‘ is waarschijnlijk slimmer dan dat. Alles wat buiten het Ascii 32-126-bereik valt, zou mijn gok zijn, maar we ‘ zouden voor de zekerheid naar de broncode moeten kijken.
- Mijn info was van de man-pagina van de specifieke grep-instantie. Uw opmerking over de implementatie is geldig, de bron overtreft docs.
- Ik had een bestand dat
grep
op cygwin als binair beschouwde omdat het een lang streepje (0x96) had in plaats van een gewoon ASCII-streepje / minus (0x2d). Ik denk dat dit antwoord het probleem van OP ‘ heeft opgelost, maar het lijkt erop dat het onvolledig is.
Antwoord
grep -a
werkte voor mij:
$ grep --help [...] -a, --text equivalent to --binary-files=text
Reacties
- Dit is het beste, minst dure antwoord IMO.
- Maar niet POSIX-compatibel
- Zou je willen uitleggen waarom dit niet zo is? Het zou goed zijn om het duidelijk te maken voor ons allemaal die dit antwoord als een optie beschouwen. Bedankt :).
- Hallo, ik ben ‘ hier een TWEEDE keer gekomen om deze LOL opnieuw te leren. Een Frans accent (diakritisch) in de tekst zorgde ervoor dat grep barf
Answer
Je kunt de strings
hulpprogramma om de tekstinhoud uit elk bestand te extraheren en deze vervolgens door grep
te leiden, zoals dit: strings file | grep pattern
.
Opmerkingen
- Ideaal voor het grepen van logbestanden die gedeeltelijk beschadigd kunnen zijn
- ja, soms binaire gemengde logboekregistratie gebeurt ook. Dit is goed.
Antwoord
GNU grep 2.24 RTFS
Conclusie: alleen 2 en 2 gevallen:
-
NUL
, bijvprintf "a\0" | grep "a"
-
coderingsfout volgens de C99
mbrlen()
, bijvoorbeeld:export LC_CTYPE="en_US.UTF-8" printf "a\x80" | grep "a"
omdat
\x80
niet de eerste byte kan zijn van een UTF-8 Unicode-punt: UTF-8 – Beschrijving | en.wikipedia.org
Verder, zoals vermeld door Stéphane Chazelas Wat maakt dat grep een bestand beschouwt binair zijn?Unix & Linux Stack Exchange , die controles worden alleen uitgevoerd tot aan de eerste gelezen buffer met een lengte TODO.
Alleen tot de eerste gelezen buffer
Dus als er een NUL of coderingsfout optreedt in het midden van een zeer groot bestand, kan dit worden toch gegrepen.
Ik veronderstel dat dit om prestatieredenen is.
Bijv .: dit drukt de regel af:
printf "%10000000s\n\x80a" | grep "a"
maar dit niet:
printf "%10s\n\x80a" | grep "a"
De werkelijke buffergrootte hangt af van hoe het bestand wordt gelezen. Bijv.vergelijk:
export LC_CTYPE="en_US.UTF-8" (printf "\n\x80a") | grep "a" (printf "\n"; sleep 1; printf "\x80a") | grep "a"
Met de sleep
wordt de eerste regel doorgegeven aan grep, zelfs als deze maar 1 byte is lang omdat het proces in slaap valt en de tweede keer lezen niet controleert of het bestand binair is.
RTFS
git clone git://git.savannah.gnu.org/grep.git cd grep git checkout v2.24
Zoek waar het stderr-foutbericht is gecodeerd:
git grep "Binary file"
Leidt ons naar /src/grep.c
:
if (!out_quiet && (encoding_error_output || (0 <= nlines_first_null && nlines_first_null < nlines))) { printf (_("Binary file %s matches\n"), filename);
Als die variabelen een goede naam hadden, kwamen we in feite tot de conclusie.
encoding_error_output
Snelle grepping voor encoding_error_output
laat zien dat het enige codepad dat het kan wijzigen door buf_has_encoding_errors
:
clen = mbrlen (p, buf + size - p, &mbs); if ((size_t) -2 <= clen) return true;
gaat en dan alleen man mbrlen
.
nlines_first_null en nlines
Geïnitialiseerd als:
intmax_t nlines_first_null = -1; nlines = 0;
dus wanneer een null wordt gevonden, wordt 0 <= nlines_first_null
waar.
TODO wanneer kan nlines_first_null < nlines
ooit vals zijn? Ik werd lui.
POSIX
Definieert geen binaire opties grep – zoek in een bestand naar een patroon | pubs.opengroup.org , en GNU grep documenteert het niet, dus RTFS is de enige manier.
Reacties
- Indrukwekkende uitleg !
- Merk op dat de controle op geldige UTF-8 alleen plaatsvindt in UTF-8-landinstellingen. Merk ook op dat de controle alleen wordt uitgevoerd op de eerste buffer die uit het bestand wordt gelezen, dat voor een normaal bestand 32768 bytes op mijn systeem lijkt te zijn, maar voor een pipe of socket kan dit zo klein zijn als één byte. Vergelijk
(printf '\n\0y') | grep y
met(printf '\n'; sleep 1; printf '\0y') | grep y
bijvoorbeeld. - @St é phaneChazelas ” Merk op dat de controle op geldige UTF-8 alleen plaatsvindt in UTF-8-landinstellingen “: bedoel je over de
export LC_CTYPE='en_US.UTF-8'
zoals in mijn voorbeeld, of iets anders? Buf gelezen: geweldig voorbeeld, toegevoegd om te beantwoorden. Je hebt de bron duidelijk meer gelezen dan ik, doet me denken aan die hacker koans ” De leerling werd ingelicht ” 🙂 - Ik heb ‘ ook niet in detail gekeken, maar zeer recentelijk
- @CiroSantilli 巴拿馬 文件 六四 事件 法轮功 tegen welke versie van GNU grep heb je getest?
Antwoord
Een van mijn tekstbestanden werd plotseling door grep als binair beschouwd:
$ file foo.txt foo.txt: ISO-8859 text
De oplossing was om het te converteren met iconv
:
iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
Reacties
- Dit is mij ook overkomen. In het bijzonder was de oorzaak een ISO-8859-1-gecodeerde niet-afbrekende spatie, die ik moest vervangen door een gewone spatie om grep in het bestand te laten zoeken.
- grep 2.21 behandelt ISO -8859 tekstbestanden alsof ze binair zijn, voeg export LC_ALL = C toe vóór het grep-commando.
- @netawater Bedankt! Dit is bijv. het geval als je zoiets als M ü ller in een tekstbestand hebt. Dat ‘ s
0xFC
hexadecimaal, dus buiten het bereik dat grep zou verwachten voor utf8 (tot0x7F
). Controleer met printf ‘ a \ x7F ‘ | grep ‘ a ‘ zoals Ciro hierboven beschrijft.
Antwoord
Het bestand /etc/magic
of /usr/share/misc/magic
heeft een lijst met reeksen waarvan het commando file
gebruikt om het bestandstype te bepalen.
Merk op dat binair bestand misschien wel een noodoplossing is. Soms worden bestanden met vreemde codering ook als binair beschouwd.
grep
op Linux heeft enkele opties om binaire bestanden te verwerken, zoals --binary-files
of -U / --binary
Reacties
- Meer precies, coderingsfout volgens C99 ‘ s
mbrlen()
. Voorbeeld en broninterpretatie op: unix.stackexchange.com/a/276028/32558
Antwoord
Een van mijn studenten had dit probleem. Er zit een bug in grep
in Cygwin
. Als het bestand niet-Ascii-tekens heeft, zien grep
en egrep
het als binair.
Reacties
- Dat klinkt als een functie, niet als een bug.Zeker gezien het feit dat er een opdrachtregeloptie is om het te besturen (-a / –text)
Answer
Beantwoord de vraag “Waarom beschouwt grep een bestand als binair?”, En gebruik iconv
:
$ iconv < myfile.java iconv: (stdin):267:70: cannot convert
In mijn geval waren er Spaanse tekens die correct werden weergegeven in teksteditors, maar grep beschouwde ze als binair; iconv
output wees me naar de regel- en kolomnummers van die karakters
In het geval van NUL
karakters, iconv
beschouwt ze als normaal en drukt dat soort uitvoer niet af, dus deze methode is niet geschikt
Antwoord
Ik had hetzelfde probleem. Ik heb vi -b [filename]
gebruikt om de toegevoegde karakters te zien. Ik heb de besturingstekens ^@
en ^M
gevonden. Typ vervolgens in vi :1,$s/^@//g
om de ^@
-tekens te verwijderen. Herhaal deze opdracht voor ^M
.
Waarschuwing: om de “blauwe” besturingstekens te krijgen, drukt u op Ctrl + v en vervolgens op Ctrl + M of Ctrl + @ . Sla vervolgens op en sluit vi af.
Answer
Ik had ook dit probleem, maar in mijn geval werd het veroorzaakt wanneer een overeenkomende regel is te lang.
file myfile.txt myfile.txt: UTF-8 Unicode text, with very long lines
grep
zou het hele bestand goed doorlopen met veel patronen, maar wanneer een patroon overeenkwam met een ” zeer lange regel ” het stopte met Binary file myfile.txt matches
.
Het toevoegen van -a
lost ook dit probleem op, maar het pre-parseren van het bestand voor NULL of andere ongeldige karakters zou geen effect hebben (er zijn er geen anders zou grep niet compleet zijn voor andere patronen). In dit geval had de overtredende regel 25k + tekens!
Wat ik niet begrijp is waarom het alleen gebeurt wanneer grep
de regel probeert terug te geven en niet wanneer deze is het aan het verwerken op zoek naar andere patronen.
--null-data
kan nuttig zijn alsNUL
is het scheidingsteken.