Jak grepować plik tekstowy zawierający dane binarne?

122

grep zwraca

Plik binarny test.log pasuje

Na przykład

echo    "line1 re \x00\r\nline2\r\nline3 re\r\n" > test.log  # in zsh
echo -e "line1 re \x00\r\nline2\r\nline3 re\r\n" > test.log  # in bash
grep re test.log

Chciałbym, żeby wynik pokazał line1 i line3 (łącznie dwie linie).

Czy jest możliwe użycie trkonwersji danych, których nie można wydrukować na dane, które można odczytać, aby grep znów działał?

Daniel YC Lin
źródło
Należy pamiętać, że istnieje program, który odfiltrowuje znaki binarne z pliku binarnego i zachowuje tylko znaki tekstowe (czytelne). Tutaj: soft.tahionic.com/download-words_extractor/index.html
InTheNameOfScience
Przepraszam, ale ... czy nie brakuje -eci echodowództwa?
Sopalajo de Arrierez
Jeśli używasz 'zsh', jest ok bez -e. Jeśli używasz 'bash', powinieneś dodać '-e'.
Daniel YC Lin
serverfault.com/questions/328101/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

67

Możesz uruchomić plik danych cat -v, np

$ cat -v tmp/test.log | grep re
line1 re ^@^M
line3 re^M

które można następnie poddać dalszej obróbce końcowej w celu usunięcia śmieci; jest to najbardziej analogiczne do zapytania dotyczącego użycia trdo zadania.

vielmetti
źródło
5
Rozwiązał mój problem. Dzięki! Oto, co man catmówi o -v:-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
tommy.carstensen
Zauważ, że działa to również w potoku. Np.set | cat -v | grep variable
funroll
1
Po co tego używać, skoro grep --text działa? Wydaje się to o wiele bardziej złożone.
Michael Haefele
grep --textnie zawsze działa; szanuje CTRL + D jako terminator pliku. Więc jeśli masz to w swoim pliku binarnym, grep zakończy działanie wcześniej.
Tommy
110
grep -a

To nie może być prostsze.

James Selvakumar
źródło
3
to jest to samo, o grep --textczym paxdiablo wspominał 2 lata wcześniej
user829755
4
Tak, z wyjątkiem tego, że to nie zadziała na OSX, chyba że wykonasz następujące czynności:LC_ALL="C" grep -a
Chris Stratton,
91

Jednym ze sposobów jest po prostu traktowanie plików binarnych jako tekstowych, grep --textale może to skutkować wysłaniem binarnych informacji do terminala. To nie jest dobry pomysł, jeśli używasz terminala, który interpretuje strumień wyjściowy (taki jak VT / DEC lub wiele innych).

Alternatywnie możesz wysłać plik za trpomocą następującego polecenia:

tr '[\000-\011\013-\037\177-\377]' '.' <test.log | grep whatever

Spowoduje to zmianę wszystkiego, co jest mniejsze niż znak spacji (z wyjątkiem nowej linii) i cokolwiek większego niż 126, w .znak, pozostawiając tylko materiały do ​​wydrukowania.


Jeśli chcesz, aby każdy „niedozwolony” znak został zastąpiony innym, możesz użyć czegoś takiego jak poniższy program w C, klasyczny standardowy filtr wejściowy:

#include<stdio.h>
int main (void) {
    int ch;
    while ((ch = getchar()) != EOF) {
        if ((ch == '\n') || ((ch >= ' ') && (ch <= '~'))) {
            putchar (ch);
        } else {
            printf ("{{%02x}}", ch);
        }
    }
    return 0;
}

To da ci {{NN}}, gdzie NNjest kod szesnastkowy znaku. Możesz po prostu dostosować printfdo dowolnego stylu wydruku.

Możesz zobaczyć ten program w akcji tutaj, gdzie:

pax$ printf 'Hello,\tBob\nGoodbye, Bob\n' | ./filterProg
Hello,{{09}}Bob
Goodbye, Bob
paxdiablo
źródło
Ta metoda mapuje wszystkie znaki binarne na te same „.” symbol. Czy istnieje inna metoda mapowania ich na czytelne symbole?
Daniel YC Lin
Jasne, możesz go uruchomić za pomocą innego programu filtrującego, z których jeden dostarczyłem w aktualizacji.
paxdiablo
1
Myślę, że tr '[:cntrl:] '.'jest lepiej. I powinno być \000-\010\013\014\016-\037\177-\377'w twojej składni tr.
Daniel YC Lin
2
Po przetestowaniu, tr '[\000-\010\013\014\016-\037\177-\377]' '_'działającym, cntrl nie jest odpowiedni dla mojego przypadku.
Daniel YC Lin
2
Możesz zapisać catkrok, podłączając grep --textdo trzamiast odwrotnie. Pozwala to również na grepowanie wielu plików i zachowanie odniesienia do nazwy pliku w wyniku.
aaaantoine
33

Możesz na przykład użyć „ciągów” do wyodrębnienia łańcuchów z pliku binarnego

strings binary.file | grep foo
moodywoody
źródło
U mnie działało dobrze, ponieważ źródłem był dziennik debugowania z UID w każdej linii. Dzięki.
mbrownnyc
dla mnie też działało dobrze. Dziękuję za odpowiedź. Uratowałem mój dzień :)
Shekhar
2
Doceniam odpowiedź @paxdiablo, ale za szybką odpowiedź i wykonanie pracy nie można tego winić.
Wil,
Próbowałem użyć rozwiązania paxdiablo, ale nie przyniosło to żadnych oczekiwanych rezultatów. @moodywoody Twoje rozwiązanie jest szybkie, proste i zapewnia dokładnie to, czego potrzebowałem!
justinhartman
20

Możesz zmusić grep do przeglądania plików binarnych za pomocą:

grep --binary-files=text

Możesz także chcieć dodać -o( --only-matching), aby nie dostać tony binarnego bełkotu, który zepsuje twój terminal.

AB
źródło
może wypisać binarne śmieci, które mogą mieć nieprzyjemne skutki uboczne, jeśli wyjście jest terminalem i jeśli sterownik terminala zinterpretuje niektóre z nich jako polecenia.
Daniel YC Lin
Jeśli używasz --only-matching, a twoje wyrażenie regularne nie pasuje do dowolnych danych binarnych, nie będziesz mieć problemu.
AB
jeśli wyrażenie regularne to „first. * end”, a dane binarne zawierają wzorzec „. *”, nie może to poprawić procesu przetwarzania końcowego. W każdym razie dzięki.
Daniel YC Lin
16

Począwszy od Grepa 2.21, pliki binarne są traktowane inaczej :

Podczas wyszukiwania danych binarnych grep może teraz traktować bajty nietekstowe jako zakończenia linii. Może to znacznie zwiększyć wydajność.

Tak więc teraz w przypadku danych binarnych wszystkie bajty nietekstowe (w tym znaki nowej linii) są traktowane jako terminatory linii. Jeśli chcesz zmienić to zachowanie, możesz:

  • używać --text. Zapewni to, że tylko znaki nowej linii będą zakończeniami linii

  • używać --null-data. Zapewni to, że tylko bajty zerowe są terminatorami linii

Steven Penny
źródło
5

grep -a zmusi grep do wyszukania i wyjścia z pliku, który grep uważa za binarny. grep -a re test.log

Kevin Buchs
źródło
3

Jak już powiedział James Selvakumar, załatwia sprawę grep -a. -a lub --text zmusza Grepa do obsługi strumienia wejściowego jako tekstu. Zobacz stronę podręcznika http://unixhelp.ed.ac.uk/CGI/man-cgi?grep

próbować

cat test.log | grep -a somestring
DerKnorr
źródło
2

możesz to zrobić

strings test.log | grep -i

spowoduje to przekonwertowanie danych wyjściowych jako czytelnego ciągu na grep.

Mrid
źródło
0

Możesz także wypróbować narzędzie Word Extractor . Word Extractor może być używany z dowolnym plikiem na komputerze w celu oddzielenia ciągów znaków zawierających tekst / słowa ludzkie od kodu binarnego (aplikacje exe, biblioteki DLL).

MattCollW
źródło
W moim przypadku nie potrzebuję ekstraktora słów, wymagam zachowania numeru wiersza.
Daniel YC Lin
0

Oto, czego użyłem w systemie, w którym nie zainstalowano polecenia „ciągi”

cat yourfilename | tr -cd "[:print:]"

To drukuje tekst i usuwa niedrukowalne znaki za jednym zamachem, w przeciwieństwie do "cat -v filename", który wymaga dodatkowego przetwarzania w celu usunięcia niechcianych elementów. Zauważ, że niektóre dane binarne mogą być wydrukowane, więc nadal będziesz mieć jakiś bełkot między dobrymi rzeczami. Myślę, że struny również usuwają ten bełkot, jeśli możesz tego użyć.

Muurder
źródło