Qu'est-ce qui fait que grep considère un fichier comme étant binaire?

185

J'ai quelques dumps de base de données d'un système Windows sur ma boîte. Ce sont des fichiers texte. J'utilise cygwin pour les remercier. Ceux-ci semblent être des fichiers de texte brut; Je les ouvre avec des éditeurs de texte tels que le bloc-notes et le wordpad. Ils ont l'air lisibles. Cependant, quand je lance grep sur eux, il dira binary file foo.txt matches.

J'ai remarqué que les fichiers contiennent des NULcaractères ascii , qui, à mon avis, sont des artefacts du dump de la base de données.

Alors, qu'est-ce qui fait que grep considère ces fichiers comme étant binaires? Le NULpersonnage? Y at-il un drapeau sur le système de fichiers? Que dois-je changer pour que grep me montre les correspondances de lignes?

utilisateur394
la source
2
--null-datapeut être utile si NULest le délimiteur.
Steve-o

Réponses:

126

S'il y a un NULcaractère n'importe où dans le fichier, grep le considérera comme un fichier binaire.

Il pourrait y avoir une solution de rechange comme celle-ci cat file | tr -d '\000' | yourgreppour éliminer tous les éléments null en premier, puis pour effectuer une recherche dans le fichier.

bbaja42
la source
149
... ou utilisez -a/ --text, du moins avec GNU grep.
derobert
1
@derobert: en fait, sur certains systèmes (plus anciens), grep voit les lignes, mais sa sortie tronquera chaque ligne correspondante à la première NUL(probablement parce qu'elle appelle la commande printf de C et lui donne la ligne correspondante?). Sur un tel système, a grep cmd .sh_historyretournera autant de lignes vides qu'il y a de lignes correspondant à 'cmd', car chaque ligne de sh_history a un format spécifique avec un NULau début de chaque ligne. (Mais votre commentaire "au moins sur GNU grep" est probablement devenu réalité. Je n'en ai pas sous la main pour le tester, mais je m'attends à ce qu'ils s'en sortent bien.)
Olivier Dulac
4
La présence d'un personnage NUL est-elle le seul critère? J'en doute. C'est probablement plus intelligent que ça. J'imagine que tout ce qui se situe en dehors de la plage Ascii 32-126 devrait être examiné, mais nous devons examiner le code source pour en être sûr.
Michael Martinez
2
Mes informations provenaient de la page de manuel de l'instance spécifique de grep. Votre commentaire sur l'implémentation est valide, la source l'emporte sur la documentation.
bbaja42
2
J'avais un fichier qui, grepsur cygwin, était considéré comme binaire parce qu'il avait un long tiret (0x96) au lieu d'un trait d'union ASCII / minus (0x2d) normal. Je suppose que cette réponse a résolu le problème du PO, mais il semble que ce soit incomplet.
cp.engr
121

grep -a a travaillé pour moi:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text
Plouff
la source
4
C’est la meilleure réponse, la moins chère, OMI.
Pydsigner
Mais pas conforme à POSIX
Matteo
21

Vous pouvez utiliser l' stringsutilitaire pour extraire le contenu du texte de tout fichier, puis l'envoie à grep, comme ceci: strings file | grep pattern.

Holgero
la source
2
Idéal pour la récupération des fichiers journaux qui pourraient être partiellement corrompus
Hannes R.
oui, il arrive parfois qu'une journalisation mixte binaire se produise. C'est bon.
sdkks
13

GNU grep 2.24 RTFS

Conclusion: 2 et 2 cas seulement:

  • NUL, par exemple printf 'a\0' | grep 'a'

  • erreur de codage selon C99 mbrlen(), par exemple:

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

    car \x80ne peut pas être le premier octet d'un point Unicode UTF-8 : UTF-8 - Description | en.wikipedia.org

De plus, comme l'a mentionné Stéphane Chazelas, qu'est-ce qui fait que grep considère un fichier comme étant binaire? | Unix et Linux Stack Exchange , ces vérifications ne sont effectuées que jusqu’à la première lecture de tampon de longueur TODO.

Seulement jusqu'à la première lecture du tampon

Ainsi, si une erreur NUL ou une erreur d'encodage se produit au milieu d'un fichier très volumineux, il se peut qu'il s'agisse d'une erreur.

J'imagine que c'est pour des raisons de performance.

Ex: ceci imprime la ligne:

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

mais cela ne veut pas:

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

La taille réelle de la mémoire tampon dépend de la façon dont le fichier est lu. Par exemple comparer:

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

Avec la sleep, la première ligne est transmise à grep même si elle ne dure qu'un octet, car le processus passe en veille, et la deuxième lecture ne vérifie pas si le fichier est binaire.

RTFS

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

Recherchez où le message d'erreur stderr est codé:

git grep 'Binary file'

Nous amène à /src/grep.c:

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

Si ces variables étaient bien nommées, nous en sommes essentiellement à la conclusion.

encoding_error_output

La recherche rapide pour encoding_error_outputindique que le seul chemin de code pouvant le modifier passe par buf_has_encoding_errors:

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

alors juste man mbrlen.

nlines_first_null et nlines

Initialisé comme:

intmax_t nlines_first_null = -1;
nlines = 0;

alors quand un nul est trouvé 0 <= nlines_first_nulldevient vrai.

TODO quand peut-il nlines_first_null < nlinesêtre faux? Je suis paresseux.

POSIX

Ne définit pas les options binaires grep - recherche dans un fichier un motif | pubs.opengroup.org , et GNU grep ne le documente pas, RTFS est donc le seul moyen.

Ciro Santilli 改造 中心 六四 事件
la source
1
Explication impressionnante!
user394
2
Notez que la vérification de la validité de UTF-8 n’est effectuée que dans les paramètres régionaux UTF-8. Notez également que la vérification est effectuée uniquement sur le premier tampon lu dans le fichier, ce qui pour un fichier normal semble être de 32 768 octets sur mon système, mais pour un tuyau ou un socket peut être aussi petit qu'un octet. Comparez (printf '\n\0y') | grep yavec (printf '\n'; sleep 1; printf '\0y') | grep ypar exemple.
Stéphane Chazelas
@ StéphaneChazelas "Notez que la vérification de la validité du format UTF-8 n'a lieu que dans les paramètres régionaux UTF-8": voulez-vous dire par export LC_CTYPE='en_US.UTF-8'exemple dans mon exemple ou quelque chose d'autre? Buf read: exemple étonnant, ajouté pour répondre. Vous avez lu évidemment la source plus que moi, me rappelle ces kôans hacker "L'étudiant était éclairé" :-)
Ciro Santilli新疆改造中心法轮功六四事件
1
Je n'ai pas regardé dans les détails non plus, mais je l'ai fait très récemment
Stéphane Chazelas
1
@CiroSantilli 文件 六四 事件 法轮功 contre quelle version de GNU grep avez-vous testé?
jrw32982
6

Un de mes fichiers texte était soudainement considéré comme binaire par grep:

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

La solution a été de le convertir en utilisant iconv:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
zzapper
la source
1
Cela m'est arrivé aussi. En particulier, la cause était un espace insécable codé ISO-8859-1, que je devais remplacer par un espace normal afin que grep puisse effectuer une recherche dans le fichier.
Gallaecio
4
grep 2.21 traite les fichiers texte ISO-8859 comme s’ils sont binaires, ajoutez export LC_ALL = C avant la commande grep.
netawater
@netawater Merci! C'est par exemple le cas si vous avez quelque chose comme Müller dans un fichier texte. C'est 0xFChexadécimal, donc en dehors de la plage attendue par utp8 (jusqu'à 0x7F). Vérifiez avec printf 'a \ x7F' | grep 'a' comme décrit par Ciro ci-dessus.
Anne van Rossum
5

Le fichier /etc/magicou /usr/share/misc/magiccontient une liste de séquences que la commande fileutilise pour déterminer le type de fichier.

Notez que le binaire peut simplement être une solution de secours. Parfois, les fichiers avec un codage étrange sont également considérés comme binaires.

grepsur Linux a quelques options pour gérer les fichiers binaires comme --binary-filesou-U / --binary

Klapaucius
la source
Plus précisément, erreur de codage selon C99 mbrlen(). Exemple interprétation et la source à l' adresse: unix.stackexchange.com/a/276028/32558
Ciro Santilli新疆改造中心法轮功六四事件
2

Un de mes étudiants a eu ce problème. Il y a un bug grepdans Cygwin. Si le fichier contient des caractères non-ascii, grepet egrepvoir comme binaire.

Joan Pontius
la source
Cela ressemble à une fonctionnalité, pas un bug. Surtout s'il existe une option en ligne de commande pour le contrôler (-a / --text)
Will Sheppard le
2

En réalité, en répondant à la question "Qu'est-ce qui fait que grep considère un fichier comme étant binaire?", Vous pouvez utiliser iconv:

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

Dans mon cas, il y avait des caractères espagnols qui apparaissaient correctement dans les éditeurs de texte mais grep les considérait comme binaires; iconvla sortie m'a pointé vers les numéros de ligne et de colonne de ces caractères

Dans le cas de NULcaractères, iconvles considérera comme normaux et n'imprimera pas ce type de sortie, donc cette méthode ne convient pas

Golimar
la source
1

J'ai eu le même problème. J'avais l'habitude vi -b [filename]de voir les caractères ajoutés. J'ai trouvé les caractères de contrôle ^@et ^M. Puis dans vi tapez :1,$s/^@//gpour supprimer les ^@caractères. Répétez cette commande pour ^M.

Avertissement: pour obtenir les caractères de contrôle "bleus", appuyez sur Ctrl+ vpuis Ctrl+ Mou Ctrl+ @. Puis enregistrez et quittez vi.

Pas certain
la source