Comment grep un fichier texte contenant des données binaires?

122

grep renvoie

Correspondances du fichier binaire test.log

Par exemple

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

Je souhaite que le résultat affiche line1 et line3 (total deux lignes).

Est-il possible d'utiliser trconvertir les données non imprimables en données lisibles, pour permettre à grep de fonctionner à nouveau?

Daniel YC Lin
la source
Veuillez noter qu'il existe un programme qui filtre les caractères binaires d'un fichier binaire et ne conserve que les caractères de texte (lisibles). Ici: soft.tahionic.com/download-words_extractor/index.html
InTheNameOfScience
Excusez-moi, mais ... ne manquez-vous pas -ela echocommande?
Sopalajo de Arrierez
Si vous utilisez 'zsh', c'est ok sans -e. Si vous utilisez «bash», vous devez ajouter «-e».
Daniel YC Lin
serverfault.com/questions/328101/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

67

Vous pouvez exécuter le fichier de données, par cat -vexemple

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

qui pourrait être ensuite post-traité pour supprimer le courrier indésirable; ceci est le plus similaire à votre requête sur l'utilisation trpour la tâche.

Vielmetti
la source
5
J'ai résolu mon problème. Merci! Voici ce que man catdit à propos de -v:-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
tommy.carstensen
Notez que cela fonctionne également dans un pipeline. Par exempleset | cat -v | grep variable
funroll le
1
Pourquoi utiliser ceci si grep --text fonctionne? Cela semble beaucoup plus complexe.
Michael Haefele
grep --textne fonctionne pas toujours; il respecte CTRL + D en tant que terminateur de fichier. Donc, si vous l'avez dans votre fichier binaire, grep se fermera tôt.
Tommy
110
grep -a

Ça ne peut pas être plus simple que ça.

James Selvakumar
la source
3
c'est le même que celui grep --textque paxdiablo a mentionné 2 ans plus tôt
user829755
4
Oui, sauf que cela ne fonctionnera pas sous OSX sauf si vous effectuez les opérations suivantes:LC_ALL="C" grep -a
Chris Stratton
91

Une façon est de simplement traiter les fichiers binaires comme du texte de toute façon, grep --textmais cela peut entraîner l'envoi d'informations binaires à votre terminal. Ce n'est pas vraiment une bonne idée si vous exécutez un terminal qui interprète le flux de sortie (tel que VT / DEC ou bien d'autres).

Vous pouvez également envoyer votre fichier via trla commande suivante:

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

Cela changera tout ce qui est inférieur à un caractère d'espace (sauf le saut de ligne) et tout ce qui est supérieur à 126 en un .caractère, ne laissant que les imprimables.


Si vous voulez que chaque caractère "illégal" soit remplacé par un autre, vous pouvez utiliser quelque chose comme le programme C suivant, un filtre d'entrée standard classique:

#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;
}

Cela vous donnera {{NN}}, où NNest le code hexadécimal du caractère. Vous pouvez simplement ajuster le printfpour le style de sortie souhaité.

Vous pouvez voir ce programme en action ici, où il:

pax$ printf 'Hello,\tBob\nGoodbye, Bob\n' | ./filterProg
Hello,{{09}}Bob
Goodbye, Bob
paxdiablo
la source
Cette méthode mappe tous les caractères binaires dans le même '.' symbole. Existe-t-il une autre méthode qui les mappe à des symboles lisibles?
Daniel YC Lin
Bien sûr, vous pouvez l'exécuter via un programme de filtrage différent, dont l'un que j'ai fourni dans une mise à jour.
paxdiablo
1
Je pense que tr '[:cntrl:] '.'c'est mieux. Et cela devrait être \000-\010\013\014\016-\037\177-\377'dans votre syntaxe tr.
Daniel YC Lin
2
Après des tests, tr '[\000-\010\013\014\016-\037\177-\377]' '_'réalisable, le cntrl ne convient pas à mon cas.
Daniel YC Lin
2
Vous pouvez enregistrer l' catétape en accédant à grep --textau trlieu de l'inverse. Cela vous permet également de greffer plusieurs fichiers et de conserver la référence du nom de fichier dans la sortie.
aaaantoine
33

Vous pouvez utiliser des "chaînes" pour extraire des chaînes d'un fichier binaire, par exemple

strings binary.file | grep foo
de mauvaise humeur
la source
Cela a bien fonctionné pour moi car la source était un journal de débogage avec UID sur chaque ligne. Merci.
mbrownnyc
a bien fonctionné pour moi aussi. Merci pour votre réponse. Sauvé ma journée :)
Shekhar
2
J'apprécie la réponse de @paxdiablo, mais pour une réponse rapide et pour continuer le travail, vous ne pouvez rien lui reprocher.
Wil
J'ai essayé d'utiliser la solution paxdiablo, mais cela ne m'a donné aucun des résultats que j'attendais. @moodywoody votre solution est rapide, simple et produit exactement ce dont j'avais besoin!
justinhartman
20

Vous pouvez forcer grep à regarder les fichiers binaires avec:

grep --binary-files=text

Vous voudrez peut-être aussi ajouter -o( --only-matching) pour ne pas avoir des tonnes de charabia binaire qui dérangeront votre terminal.

UN B
la source
peut générer des déchets binaires, ce qui peut avoir des effets secondaires désagréables si la sortie est un terminal et si le pilote de terminal en interprète une partie comme des commandes.
Daniel YC Lin
Si vous utilisez --only-matchinget que votre regex ne correspond pas à des données binaires arbitraires, vous n'aurez pas de problème.
AB
si l'expression régulière est «first. * end» et que les données binaires contiennent un motif «. *», cela ne peut pas corriger le processus pour mon post-traitement. Bref, merci.
Daniel YC Lin
16

À partir de Grep 2.21, les fichiers binaires sont traités différemment :

Lors de la recherche de données binaires, grep peut désormais traiter les octets non textuels comme des terminateurs de ligne. Cela peut améliorer considérablement les performances.

Donc, ce qui se passe maintenant, c'est qu'avec les données binaires, tous les octets non textuels (y compris les retours à la ligne) sont traités comme des terminateurs de ligne. Si vous souhaitez modifier ce comportement, vous pouvez:

  • utiliser --text. Cela garantira que seules les nouvelles lignes sont des terminateurs de ligne

  • utiliser --null-data. Cela garantira que seuls les octets nuls sont des terminateurs de ligne

Steven Penny
la source
5

grep -a forcera grep à rechercher et à sortir d'un fichier que grep pense être binaire. grep -a re test.log

Kevin Buchs
la source
3

Comme James Selvakumar l'a déjà dit, grep -afait l'affaire. -a ou --text force Grep à gérer le flux d'entrée sous forme de texte. Voir la page de manuel http://unixhelp.ed.ac.uk/CGI/man-cgi?grep

essayer

cat test.log | grep -a somestring
DerKnorr
la source
2

tu peux faire

strings test.log | grep -i

cela convertira la sortie de données sous forme de chaîne lisible en grep.

Mrid
la source
0

Vous pouvez également essayer l' outil Word Extractor . Word Extractor peut être utilisé avec n'importe quel fichier de votre ordinateur pour séparer les chaînes contenant du texte / des mots humains du code binaire (applications exe, DLL).

MattCollW
la source
Dans mon cas, je n'ai pas besoin d'un extracteur de mots, j'ai besoin de garder le numéro de ligne.
Daniel YC Lin
0

Voici ce que j'ai utilisé dans un système sur lequel la commande "strings" n'était pas installée

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

Cela imprime le texte et supprime les caractères non imprimables d'un seul coup, contrairement à "cat -v filename" qui nécessite un post-traitement pour supprimer les éléments indésirables. Notez que certaines des données binaires peuvent être imprimables, vous aurez donc toujours du charabia entre les bonnes choses. Je pense que les cordes suppriment également ce charabia si vous pouvez l'utiliser.

Muurder
la source