Esta es la solución de línea única solicitada (para shells recientes que tienen "sustitución de proceso"):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
Si no hay una "sustitución de proceso" <(…)
disponible, solo use grep como filtro:
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
A continuación se muestra la descripción detallada de cada parte de la solución.
Valores de bytes de números hexadecimales:
Su primer problema es fácil de resolver:
Esas secuencias de escape \ Xnn solo funcionan en la concha de pescado.
Cambie el superior X
a uno inferior x
y use printf (para la mayoría de los shells):
$ printf -- '\xef\xbe\xad\xde'
O usar:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
Para aquellos shells que eligen no implementar la representación '\ x'.
Por supuesto, traducir hexadecimal a octal funcionará en (casi) cualquier shell:
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
Donde "$ sh" es cualquier shell (razonable). Pero es bastante difícil mantenerlo correctamente citado.
Archivos binarios.
La solución más sólida es transformar el archivo y la secuencia de bytes (ambos) en alguna codificación que no tenga problemas con valores de caracteres impares como (nueva línea) 0x0A
o (byte nulo) 0x00
. Ambos son bastante difíciles de administrar correctamente con herramientas diseñadas y adaptadas para procesar "archivos de texto".
Una transformación como base64 puede parecer válida, pero presenta el problema de que cada byte de entrada puede tener hasta tres representaciones de salida dependiendo de si es el primer, segundo o tercer byte de la posición mod 24 (bits).
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
Transformación hexadecimal.
Es por eso que la transformación más robusta debería ser una que comience en cada límite de byte, como la simple representación HEX.
Podemos obtener un archivo con la representación hexadecimal del archivo con cualquiera de estas herramientas:
$ 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 secuencia de bytes para buscar ya está en hexadecimal en este caso.
:
$ var="ef be ad de"
Pero también podría ser transformado. A continuación se muestra un ejemplo de ida y vuelta hex-bin-hex:
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
La cadena de búsqueda se puede establecer a partir de la representación binaria. Cualquiera de las tres opciones presentadas anteriormente od, hexdump o xxd son equivalentes. Solo asegúrese de incluir los espacios para asegurarse de que la coincidencia esté en los límites de bytes (no se permite el cambio de mordisco):
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
Si el archivo binario se ve así:
$ 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
Luego, una simple búsqueda grep dará la lista de secuencias coincidentes:
$ grep -o "$a" infile.hex | wc -l
2
¿Una línea?
Todo se puede realizar en una línea:
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
Por ejemplo, buscar 11221122
en el mismo archivo necesitará estos dos pasos:
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
Para "ver" los partidos:
$ 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
Tamponamiento
Existe la preocupación de que grep almacenará en el búfer todo el archivo y, si el archivo es grande, creará una gran carga para la computadora. Para eso, podemos usar una solución sed no tamponada:
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
El primer sed no tiene búfer ( -u
) y se usa solo para inyectar dos nuevas líneas en la secuencia por cadena coincidente. El segundo sed
solo imprimirá las líneas coincidentes (cortas). El wc -l contará las líneas coincidentes.
Esto almacenará solo algunas líneas cortas. Las cadenas coincidentes en el segundo sed. Esto debería ser bastante bajo en recursos utilizados.
O, algo más complejo de entender, pero la misma idea en 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