V mém boxu mám nějaké skládky databází ze systému Windows. Jsou to textové soubory. K jejich procházení používám cygwin. Zdá se, že jde o soubory s prostým textem; otevírám je textovými editory, jako jsou poznámkový blok a wordpad, a vypadají čitelně. Když na ně ale spustím grep, řekne to binary file foo.txt matches
.
Všiml jsem si, že soubory obsahují některé znaky ascii NUL
, které jsou podle mého názoru artefakty z výpisu databáze.
Proč tedy grep považuje tyto soubory za binární? Znak NUL
? Je v souborovém systému příznak? Co musím změnit, abych získal grep ukázat mi shodu řádku?
Komentáře
Odpověď
Pokud existuje NUL
znak kdekoli v souboru, grep jej bude považovat za binární soubor.
Může existovat takové řešení cat file | tr -d "\000" | yourgrep
k odstranění nejdříve všechny null a poté prohledejte soubor.
Komentáře
- … nebo použijte
-a
/--text
, alespoň s GNU grep. - @derobert: ve skutečnosti na některých (starších) systémech grep vidí řádky, ale jeho výstup zkrátí každý odpovídající řádek na prvním
NUL
(pravděpodobně proto, že volá C ‚ s printf a dá mu odpovídající řádek?). V takovém systémugrep cmd .sh_history
vrátí tolik prázdných řádků, kolik řádků odpovídá ‚ cmd ‚, protože každý řádek sh_history má specifický formát sNUL
na začátku každého řádku. (ale váš komentář “ alespoň na GNU grep “ se pravděpodobně splní. ‚ právě teď nemáte po ruce k testování, ale předpokládám, že to zvládnou pěkně) - Je přítomnost znaku NUL jediným kritériem? Pochybuji. Je to ‚ pravděpodobně chytřejší. Cokoli, co spadá mimo rozsah Ascii 32-126, by byl můj odhad, ale ‚ si pro jistotu musíme prohlédnout zdrojový kód.
- Moje informace byla z manuálové stránky konkrétní instance grep. Váš komentář k implementaci je platný, zdroj trumfuje dokumenty.
- Měl jsem soubor, který
grep
na cygwin považoval za binární, protože místo něj měl dlouhou pomlčku (0x96) běžný pomlčka ASCII / minus (0x2d). Myslím, že tato odpověď vyřešila problém OP ‚ s, ale zdá se, že je neúplný.
Odpověď
grep -a
pro mě pracoval:
$ grep --help [...] -a, --text equivalent to --binary-files=text
Komentáře
- Toto je nejlepší a nejlevnější odpověď IMO.
- Ale není kompatibilní s POSIX
- Chtěli byste vysvětlit, proč tomu tak není? Bylo by dobré objasnit to pro nás všechny, kteří tuto odpověď považujeme za jednu z možností. Díky :).
- Ahoj, ‚ jsem sem přišel DRUHÝ čas, abych se znovu naučil tuto LOL. Francouzský přízvuk (diakritika) v textu způsobil, že grep zablokoval
odpověď
Můžete použít strings
k extrakci textového obsahu z libovolného souboru a jeho následnému propojení grep
, například takto: strings file | grep pattern
.
Komentáře
- Ideální pro grepování souborů protokolu, které mohou být částečně poškozené
- ano, někdy binární smíšené protokolování také se stane. To je dobré.
Odpověď
GNU grep 2.24 RTFS
Závěr: Pouze případy 2 a 2:
-
NUL
, napřprintf "a\0" | grep "a"
-
chyba kódování podle C99
mbrlen()
, např .:export LC_CTYPE="en_US.UTF-8" printf "a\x80" | grep "a"
protože
\x80
nemůže být prvním bajtem bodu Unicode UTF-8: UTF-8 – Popis | en.wikipedia.org
Dále, jak uvádí Stéphane Chazelas Co dělá grep považován za soubor být binární? | Unix & Linux Stack Exchange , tyto kontroly se provádějí pouze do prvního přečtení vyrovnávací paměti o délce TODO.
Přečíst pouze do první vyrovnávací paměti
Pokud tedy dojde k chybě NUL nebo chybě kódování uprostřed velmi velkého souboru, může být stejně pozdraven.
Představuji si, že je to z důvodů výkonu.
Např .: tímto se vytiskne řádek:
printf "%10000000s\n\x80a" | grep "a"
ale to není:
printf "%10s\n\x80a" | grep "a"
Skutečná velikost vyrovnávací paměti závisí na tom, jak je soubor načten. Např.porovnat:
export LC_CTYPE="en_US.UTF-8" (printf "\n\x80a") | grep "a" (printf "\n"; sleep 1; printf "\x80a") | grep "a"
U sleep
se první řádek předá grepu, i když má pouze 1 bajt dlouho, protože proces přejde do režimu spánku a druhé čtení nekontroluje, zda je soubor binární.
RTFS
git clone git://git.savannah.gnu.org/grep.git cd grep git checkout v2.24
Vyhledat, kde je zakódována chybová zpráva stderr:
git grep "Binary file"
Vede nás k /src/grep.c
:
if (!out_quiet && (encoding_error_output || (0 <= nlines_first_null && nlines_first_null < nlines))) { printf (_("Binary file %s matches\n"), filename);
Pokud byly tyto proměnné dobře pojmenovány, dospěli jsme v zásadě k závěru.
encoding_error_output
Rychlý grep pro encoding_error_output
ukazuje, že jediná cesta kódu, která ji může upravit, prochází buf_has_encoding_errors
:
clen = mbrlen (p, buf + size - p, &mbs); if ((size_t) -2 <= clen) return true;
a pak pouze man mbrlen
.
nlines_first_null a nlines
Inicializováno jako:
intmax_t nlines_first_null = -1; nlines = 0;
takže když je nalezena null, 0 <= nlines_first_null
se stane pravdou.
TODO kdy může nlines_first_null < nlines
být nepravdivý? Zlenivěl jsem.
POSIX
Nedefinuje binární možnosti grep – vyhledat soubor pro vzor | pubs.opengroup.org a GNU grep to nezdokumentuje, takže RTFS je jediný způsob.
Komentáře
- Působivé vysvětlení !
- Upozorňujeme, že kontrola platného UTF-8 se děje pouze v národních prostředích UTF-8. Všimněte si také, že kontrola se provádí pouze na první vyrovnávací paměti načtené ze souboru, který se u běžného souboru v mém systému zdá být 32768 bajtů, ale pro potrubí nebo zásuvku může být tak malý jako jeden bajt. Porovnejte například
(printf '\n\0y') | grep y
s(printf '\n'; sleep 1; printf '\0y') | grep y
. - @St é phaneChazelas “ Upozorňujeme, že kontrola platného UTF-8 se děje pouze v národních prostředích UTF-8 „: myslíte tím o
export LC_CTYPE='en_US.UTF-8'
jako v mém příkladu, nebo něco jiného? Buf číst: úžasný příklad, přidán k odpovědi. Zjevně jste přečetli zdroj více než já, připomíná mi ty hackerské koany “ Student byl osvícený “ 🙂 - Ani jsem se ‚ moc podrobně nezabýval, ale udělal jste to nedávno
- @CiroSantilli 巴拿馬 文件 六四 事件 法轮功 proti jaké verzi GNU grep jste testovali?
Odpověď
Jeden z mých textových souborů byl grepem najednou viděn jako binární:
$ file foo.txt foo.txt: ISO-8859 text
Řešením bylo převést jej pomocí iconv
:
iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
komentářů
- To se stalo i mně. Příčinou byl zejména nerozbitný prostor kódovaný podle ISO-8859-1, který jsem musel nahradit běžným prostorem, abych získal grep pro vyhledávání v souboru.
- grep 2.21 zachází s ISO -8859 textových souborů, jako by byly binární, přidejte před příkazem grep export LC_ALL = C.
- @netawater Díky! To je např. v případě, že máte v textovém souboru něco jako M ü ller. To ‚ s
0xFC
šestnáctkové, takže mimo rozsah by grep očekával pro utf8 (až0x7F
). Zkontrolujte pomocí printf ‚ a \ x7F ‚ | grep ‚ a ‚ jak popisuje Ciro výše.
Odpověď
Soubor /etc/magic
nebo /usr/share/misc/magic
obsahuje seznam sekvencí, které příkaz file
slouží k určení typu souboru.
Všimněte si , že binární soubor může být pouze záložním řešením. Někdy se soubory s podivným kódováním považují také za binární.
grep
v systému Linux má některé možnosti zpracování binárních souborů, jako je --binary-files
nebo -U / --binary
Komentáře
- Přesněji řečeno, chyba kódování podle C99 ‚ s
mbrlen()
. Příklad a výklad zdroje na: unix.stackexchange.com/a/276028/32558
Odpovědět
Jeden z mých studentů měl tento problém. V grep
je chyba v Cygwin
. Pokud soubor obsahuje jiné znaky než Ascii, považujte jej grep
a egrep
za binární.
Komentáře
- To zní jako funkce, nikoli jako chyba.Obzvláště vzhledem k možnosti jeho ovládání pomocí příkazového řádku (-a / –text)
Odpověď
Ve skutečnosti na otázku „Co dělá grep, aby považoval soubor za binární?“, Můžete použít iconv
:
$ iconv < myfile.java iconv: (stdin):267:70: cannot convert
V mém případě byly španělské znaky, které se v textových editorech zobrazily správně, ale grep je považoval za binární; iconv
výstup mě upozornil na čísla řádků a sloupců těchto znaků
V případě NUL
znaků iconv
je bude považovat za normální a nevytiskne tento druh výstupu, takže tato metoda není vhodná
Odpověď
Měl jsem stejný problém. Použil jsem vi -b [filename]
k zobrazení přidaných znaků. Našel jsem kontrolní znaky ^@
a ^M
. Potom ve vi zadejte :1,$s/^@//g
a odstraňte ^@
znaky. Tento příkaz opakujte pro ^M
.
Upozornění: Chcete-li získat „modré“ kontrolní znaky, stiskněte Ctrl + v a poté Ctrl + M nebo Ctrl + @ . Pak uložte a ukončete vi.
Odpověď
Také jsem měl tento problém, ale v mém případě to bylo způsobeno, když je shodná linka příliš dlouhý.
file myfile.txt myfile.txt: UTF-8 Unicode text, with very long lines
grep
by běžel celým souborem v pořádku s mnoha vzory, ale pokud by se vzor shodoval s “ velmi dlouhá řada “ se zastavila u Binary file myfile.txt matches
.
Přidání -a
tento problém také řeší, ale předběžná analýza souboru pro NULL nebo jiné neplatné znaky by neměla žádný účinek (neexistují žádné, jinak by se grep nedokončil pro jiné vzory). V tomto případě měla problematická linka 25k + znaků!
To, čemu nerozumím, je důvod, proč se to stane, jen když se grep
pokusí linku vrátit, a ne když zpracovává to a hledá další vzory.
--null-data
mohou být užitečné, pokudNUL
je oddělovač.