Il s'agit de la solution monoplace demandée (pour les shells récents qui ont une "substitution de processus"):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
Si aucune "substitution de processus" <(…)
n'est disponible, utilisez simplement grep comme filtre:
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
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:
Ces séquences d'échappement \ Xnn ne fonctionnent que dans la coquille du poisson.
Changez le haut X
en bas x
et utilisez printf (pour la plupart des shells):
$ printf -- '\xef\xbe\xad\xde'
Ou utiliser:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
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:
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
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).
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
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:
$ od -vAn -tx1 infile.bin | tr -d '\n' > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' ' > infile.hex
La séquence d'octets à rechercher est déjà en hexadécimal dans ce cas.
:
$ var="ef be ad de"
Mais il pourrait aussi être transformé. Voici un exemple d'hex-bin-hex aller-retour:
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
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é):
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
Si le fichier binaire ressemble à ceci:
$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074 This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70 est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120 ut ......from a
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131 bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000060: 3232 0a
Ensuite, une simple recherche grep donnera la liste des séquences correspondantes:
$ grep -o "$a" infile.hex | wc -l
2
Une ligne?
Tout peut être exécuté sur une seule ligne:
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
Par exemple, la recherche 11221122
dans le même fichier nécessitera ces deux étapes:
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
Pour "voir" les matchs:
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232
$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
… 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:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -ue 's/\('"$a"'\)/\n\1\n/g' |
sed -n '/^'"$a"'$/p' |
wc -l
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 seconde sed
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:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
wc -l
grep -o