Je veux compter combien de fois une certaine séquence d'octets se produit dans un fichier que j'ai. Par exemple, je veux savoir combien de fois le nombre \0xdeadbeef
se produit dans un fichier exécutable. En ce moment, je fais cela en utilisant grep:
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(Les octets sont écrits dans l'ordre inverse car mon processeur est peu endian)
Cependant, j'ai deux problèmes avec mon approche:
- Ces
\Xnn
séquences d'échappement ne fonctionnent que dans la coquille du poisson. - grep compte en fait le nombre de lignes qui contiennent mon nombre magique. Si le motif se produit deux fois sur la même ligne, il ne comptera qu'une seule fois.
Existe-t-il un moyen de résoudre ces problèmes? Comment puis-je faire fonctionner cette doublure dans le shell Bash et compter avec précision le nombre de fois que le motif se produit à l'intérieur du fichier?
bash
grep
escape-characters
hugomg
la source
la source
grep -o
11221122
, que doit-on renvoyer sur une entrée comme112211221122
? 1 ou 2?Réponses:
Il s'agit de la solution monoplace demandée (pour les shells récents qui ont une "substitution de processus"):
Si aucune "substitution de processus"
<(…)
n'est disponible, utilisez simplement grep comme filtre:Vous trouverez ci-dessous la description détaillée de chaque partie de la solution.
Valeurs d'octets à partir de nombres hexadécimaux:
Votre premier problème est facile à résoudre:
Changez le haut
X
en basx
et utilisez printf (pour la plupart des shells):Ou utiliser:
Pour les shells qui choisissent de ne pas implémenter la représentation '\ x'.
Bien sûr, traduire hex en octal fonctionnera sur (presque) n'importe quel shell:
Où "$ sh" est un shell (raisonnable). Mais il est assez difficile de le garder correctement cité.
Fichiers binaires.
La solution la plus robuste consiste à transformer le fichier et la séquence d'octets (les deux) en un encodage qui n'a aucun problème avec les valeurs de caractères impaires comme (nouvelle ligne)
0x0A
ou (octet nul)0x00
. Les deux sont assez difficiles à gérer correctement avec des outils conçus et adaptés pour traiter des "fichiers texte".Une transformation comme base64 peut sembler valide, mais elle présente le problème que chaque octet d'entrée peut avoir jusqu'à trois représentations de sortie selon qu'il s'agit du premier, du deuxième ou du troisième octet de la position du mod 24 (bits).
Transformation hexagonale.
C'est pourquoi la transformation la plus robuste devrait être celle qui commence sur chaque frontière d'octet, comme la simple représentation HEX.
Nous pouvons obtenir un fichier avec la représentation hexadécimale du fichier avec l'un ou l'autre de ces outils:
La séquence d'octets à rechercher est déjà en hexadécimal dans ce cas.
:
Mais il pourrait aussi être transformé. Voici un exemple d'hex-bin-hex aller-retour:
La chaîne de recherche peut être définie à partir de la représentation binaire. Chacune des trois options présentées ci-dessus od, hexdump ou xxd sont équivalentes. Assurez-vous simplement d'inclure les espaces pour vous assurer que la correspondance est sur les limites d'octets (aucun décalage de quartet autorisé):
Si le fichier binaire ressemble à ceci:
Ensuite, une simple recherche grep donnera la liste des séquences correspondantes:
Une ligne?
Tout peut être exécuté sur une seule ligne:
Par exemple, la recherche
11221122
dans le même fichier nécessitera ces deux étapes:Pour "voir" les matchs:
… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a
Mise en mémoire tampon
Il est à craindre que grep ne mette en mémoire tampon tout le fichier et, si le fichier est volumineux, crée une lourde charge pour l'ordinateur. Pour cela, nous pouvons utiliser une solution sed sans tampon:
Le premier sed est sans tampon (
-u
) et n'est utilisé que pour injecter deux sauts de ligne sur le flux par chaîne correspondante. La secondesed
n'imprimera que les (courtes) lignes correspondantes. Le wc -l comptera les lignes correspondantes.Cela ne mettra en mémoire tampon que quelques lignes courtes. La ou les cordes correspondantes dans le deuxième sed. Cela devrait être assez faible dans les ressources utilisées.
Ou, un peu plus complexe à comprendre, mais la même idée dans un sed:
la source
grep
qu'il finira par le charger en entier (ici deux fois la taille du fichier d'origine + 1 à cause de l'encodage hexadécimal), donc à la fin, cela finit par être plus les frais généraux que l'python
approche ouperl
celui avec-0777
. Vous avez également besoin d'unegrep
implémentation qui prend en charge les lignes de longueur arbitraire (celles qui le prennent-o
généralement en charge ). Bonne réponse sinon.od -An -tx1 | tr -d '\n'
ouhexdump -v -e '/1 " %02x"'
avec une chaîne de recherche contenant également des espaces, évitez cela, mais je ne vois pas de tel correctif pourxxd
.sed -u
(le cas échéant) est pour la mémoire tampon. Cela signifie qu'il lira un octet à la fois en entrée et produira sa sortie immédiatement sans mise en mémoire tampon. Dans tous les cas, il faudra toujours charger toute la ligne dans l'espace de motif, donc cela ne sera pas utile ici.Avec GNU
grep
de-P
(perl-regexp) drapeauLC_ALL=C
est d'éviter les problèmes dans les paramètres régionaux multi-octetsgrep
qui, sinon, essaieraient d'interpréter des séquences d'octets comme des caractères.-a
traite les fichiers binaires équivalents aux fichiers texte (au lieu du comportement normal, oùgrep
ne s'affiche que s'il y a au moins une correspondance ou non)la source
grep
le faire correspondre?-a
option, sinon grep répondra avecBinary file file.bin matches
pour tout fichier que grep détecte comme binaire.Qui traite le (s) fichier (s) d'entrée comme binaire (pas de traduction pour les sauts de ligne ou les encodages, voir perlrun ) puis boucle sur le (s) fichier (s) d'entrée n'imprimant pas en incrémentant un compteur pour toutes les correspondances de l'hex donné (ou quelle que soit la forme, voir perlre ) .
la source
-0ooo
).$/
, avec un compromis légèrement différent (utilisation de la mémoire proportionnelle à la distance maximale entre de telles séquences):perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
Avec GNU
awk
, vous pouvez faire:Si l'un des octets est un opérateur ERE, il doit être échappé (avec
\\
). Comme0x2e
ce qui.
devrait être entré comme\\.
ou\\\x2e
. En dehors de cela, cela devrait fonctionner avec des valeurs d'octets arbitraires, y compris 0 et 0xa.Notez que ce n'est pas aussi simple que simplement
NR-1
parce qu'il existe quelques cas spéciaux:RT==""
.Notez également que dans le pire des cas (si le fichier ne contient pas le terme de recherche), le fichier finira par être chargé entier en mémoire).
la source
La traduction la plus simple que je vois est:
Là où je l' ai utilisé
$'\xef'
comme bash ANSI-citant ( à l' origine uneksh93
fonctionnalité, désormais pris en charge parzsh
,bash
,mksh
, FreeBSDsh
version) de ce poisson\Xef
, et utiliségrep -o ... | wc -l
pour compter les cas.grep -o
affiche chaque correspondance sur une ligne distincte. Le-a
drapeau fait que grep se comporte sur les fichiers binaires de la même manière que sur les fichiers texte.-F
est pour les chaînes fixes, vous n'avez donc pas besoin d'échapper aux opérateurs regex.Comme dans votre
fish
cas, vous ne pouvez pas utiliser cette approche si la séquence à rechercher inclut les octets 0 ou 0xa (nouvelle ligne en ASCII).la source
printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'
serait la méthode la plus portable de "pure shell". Bien sûr: celaprintf "efbeadde" | xxd -p -r > hugohex
semble être la méthode la plus pratique.Vous pouvez utiliser la
bytes.count
méthode de Python pour obtenir le nombre total de sous-chaînes qui ne se chevauchent pas dans un bytestring.Ce one-liner chargera le fichier entier en mémoire, donc pas le plus efficace, mais fonctionne et est plus lisible que Perl; D
la source
239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=
(gd & r)mmap()
un fichier en Python ; cela réduirait la validation de la mémoire.la source
Je pense que vous pouvez utiliser Perl, essayez-le:
Remplacer la commande
s
donne le nombre de remplacements effectués, -0777 signifie ne pas traiter la nouvelle ligne comme un caractère spécial,e
- exécuter la commande,say
pour imprimer ce qui va ensuite puis imprimer le nouveau caractère de ligne,n
je n'avais pas complètement saisi, mais ne fonctionne pas sans - à partir de documents:la source