Questa è la soluzione one-liner richiesta (per shell recenti che hanno "sostituzione di processo"):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
Se non <(…)
è disponibile alcuna "sostituzione di processo" , basta usare grep come filtro:
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
Di seguito è la descrizione dettagliata di ogni parte della soluzione.
Valori byte da numeri esadecimali:
Il tuo primo problema è facile da risolvere:
Quelle sequenze di escape \ Xnn funzionano solo nel guscio di pesce.
Cambia la parte superiore X
con una inferiore x
e usa printf (per la maggior parte delle shell):
$ printf -- '\xef\xbe\xad\xde'
Oppure usa:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
Per quelle shell che scelgono di non implementare la rappresentazione '\ x'.
Ovviamente, tradurre hex in ottale funzionerà su (quasi) qualsiasi shell:
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
Dove "$ sh" è qualsiasi shell (ragionevole). Ma è abbastanza difficile tenerlo correttamente citato.
File binari.
La soluzione più efficace è trasformare il file e la sequenza di byte (entrambi) in una codifica che non abbia problemi con valori di caratteri dispari come (nuova riga) 0x0A
o (byte null) 0x00
. Entrambi sono piuttosto difficili da gestire correttamente con strumenti progettati e adattati per elaborare "file di testo".
Una trasformazione come base64 può sembrare valida, ma presenta il problema che ogni byte di input può avere fino a tre rappresentazioni di output a seconda che si tratti del primo, secondo o terzo byte della posizione mod 24 (bit).
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
Trasformazione esadecimale.
Ecco perché la trasformazione più affidabile dovrebbe essere quella che inizia su ogni limite di byte, come la semplice rappresentazione HEX.
Possiamo ottenere un file con la rappresentazione esadecimale del file con uno di questi strumenti:
$ 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
In questo caso la sequenza di byte da cercare è già in esadecimale.
:
$ var="ef be ad de"
Ma potrebbe anche essere trasformato. Segue un esempio di hex-bin-hex esadecimale di andata e ritorno:
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
La stringa di ricerca può essere impostata dalla rappresentazione binaria. Una delle tre opzioni presentate sopra od, hexdump o xxd sono equivalenti. Assicurati solo di includere gli spazi per assicurarti che la corrispondenza si trovi sui confini dei byte (non è consentito alcuno spostamento nibble):
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
Se il file binario è simile al seguente:
$ 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
Quindi, una semplice ricerca grep fornirà l'elenco delle sequenze corrispondenti:
$ grep -o "$a" infile.hex | wc -l
2
Una linea?
Tutto può essere eseguito su una riga:
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
Ad esempio, la ricerca 11221122
nello stesso file richiede questi due passaggi:
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
Per "vedere" le partite:
$ 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
buffering
C'è il timore che grep esegua il buffer dell'intero file e, se il file è grande, crea un carico pesante per il computer. Per questo, possiamo usare una soluzione sed senza buffer:
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
Il primo sed è senza buffer ( -u
) e viene utilizzato solo per iniettare due nuove righe sullo stream per stringa corrispondente. Il secondo sed
stamperà solo le linee (brevi) corrispondenti. Il wc -l conterà le linee corrispondenti.
Questo buffererà solo alcune righe brevi. Le stringhe corrispondenti nella seconda seduta. Questo dovrebbe essere abbastanza basso nelle risorse utilizzate.
O, un po 'più complesso da capire, ma la stessa idea in una 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