Ich möchte zählen, wie oft eine bestimmte Folge von Bytes in einer Datei vorkommt, die ich habe. Zum Beispiel möchte ich herausfinden, wie oft die Nummer \0xdeadbeef
in einer ausführbaren Datei vorkommt. Im Moment mache ich das mit grep:
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(Die Bytes werden in umgekehrter Reihenfolge geschrieben, da meine CPU Little-Endian ist.)
Ich habe jedoch zwei Probleme mit meinem Ansatz:
- Diese
\Xnn
Fluchtsequenzen funktionieren nur in der Fischhülle. - grep zählt tatsächlich die Anzahl der Zeilen, die meine magische Zahl enthalten. Wenn das Muster zweimal in derselben Zeile vorkommt, zählt es nur einmal.
Gibt es eine Möglichkeit, diese Probleme zu beheben? Wie kann ich diesen einen Liner in der Bash-Shell laufen lassen und genau zählen, wie oft das Muster in der Datei vorkommt?
bash
grep
escape-characters
hugomg
quelle
quelle
grep -o
11221122
, wie sollte sie bei einer Eingabe zurückgegeben werden112211221122
? 1 oder 2?Antworten:
Dies ist die angeforderte Ein-Liner-Lösung (für aktuelle Shells mit "Prozesssubstitution"):
Wenn keine "Prozessersetzung"
<(…)
verfügbar ist, verwenden Sie einfach grep als Filter:Nachstehend finden Sie eine detaillierte Beschreibung der einzelnen Teile der Lösung.
Byte-Werte aus Hex-Zahlen:
Ihr erstes Problem ist leicht zu lösen:
Ändern Sie die obere
X
in eine unterex
und verwenden Sie printf (für die meisten Shells):Oder benutze:
Für diejenigen Shells, die die Darstellung '\ x' nicht implementieren möchten.
Natürlich funktioniert die Übersetzung von Hex nach Oktal auf (fast) jeder Shell:
Wobei "$ sh" eine beliebige (vernünftige) Shell ist. Aber es ist ziemlich schwierig, es richtig zu zitieren.
Binärdateien.
Die robusteste Lösung besteht darin, die Datei und die Bytefolge (beide) in eine Codierung umzuwandeln, die keine Probleme mit ungeraden Zeichenwerten wie (neue Zeile)
0x0A
oder (Null-Byte) aufweist0x00
. Beide sind mit Tools, die für die Verarbeitung von "Textdateien" entwickelt und angepasst wurden, nur schwer korrekt zu verwalten.Eine Transformation wie base64 mag als gültig erscheinen, birgt jedoch das Problem, dass jedes Eingabebyte bis zu drei Ausgabedarstellungen haben kann, je nachdem, ob es sich um das erste, zweite oder dritte Byte der Mod-24-Position (Bits) handelt.
Verhexung transformieren.
Deshalb sollte die robusteste Transformation eine sein, die an jeder Byte-Grenze beginnt, wie die einfache HEX-Darstellung.
Wir können eine Datei mit der hexadezimalen Darstellung der Datei mit jedem dieser Tools erhalten:
Die zu durchsuchende Bytefolge ist in diesem Fall bereits hexadezimal.
:
Es könnte aber auch transformiert werden. Ein Beispiel für eine Rundreise hex-bin-hex folgt:
Der Suchstring kann aus der Binärdarstellung gesetzt werden. Alle drei oben aufgeführten Optionen od, hexdump oder xxd sind gleichwertig. Stellen Sie einfach sicher, dass die Leerzeichen enthalten sind, um sicherzustellen, dass die Übereinstimmung an den Byte-Grenzen liegt (keine Nibble-Verschiebung zulässig):
Wenn die Binärdatei so aussieht:
Dann gibt eine einfache Suche nach grep die Liste der übereinstimmenden Sequenzen aus:
Eine Linie?
Es kann alles in einer Zeile ausgeführt werden:
Für die Suche
11221122
in derselben Datei sind beispielsweise die folgenden zwei Schritte erforderlich:So "sehen" Sie die Übereinstimmungen:
… 0a 31313232313132323131323231313232313132323131323131323231313232 313132320a
Pufferung
Es besteht die Sorge, dass grep die gesamte Datei puffert und, wenn die Datei groß ist, eine hohe Last für den Computer erstellt. Dafür können wir eine ungepufferte sed-Lösung verwenden:
Die erste sed ist ungepuffert (
-u
) und wird nur zum Einfügen von zwei Zeilenumbrüchen in den Stream pro übereinstimmender Zeichenfolge verwendet. Die Sekundesed
wird nur die (kurzen) übereinstimmenden Zeilen drucken. Das wc -l zählt die übereinstimmenden Zeilen.Dies puffert nur einige kurze Zeilen. Die passende (n) Saite (n) im zweiten Satz. Dies sollte relativ ressourcenschonend sein.
Oder etwas komplexer zu verstehen, aber die gleiche Idee in einem Satz:
quelle
grep
in den Speicher geladen wird (hier doppelt so groß wie die ursprüngliche Datei + 1 aufgrund der Hexadezimalcodierung) Overhead als derpython
Ansatz oder derperl
mit-0777
. Sie benötigen auch einegrep
Implementierung, die Zeilen beliebiger Länge unterstützt (die, die im-o
Allgemeinen unterstützen). Gute Antwort ansonsten.od -An -tx1 | tr -d '\n'
oderhexdump -v -e '/1 " %02x"'
mit einer Suchzeichenfolge, die auch Leerzeichen enthält, vermeiden Sie dies, aber ich sehe keine solche Lösung fürxxd
.sed -u
(wo verfügbar) dient zum Entpuffern. Das bedeutet, dass es bei der Eingabe jeweils ein Byte liest und seine Ausgabe sofort ohne Pufferung ausgibt. In jedem Fall muss immer noch die gesamte Zeile in den Musterbereich geladen werden, was hier nicht weiterhilft.Mit GNU
grep
‚s-P
(perl-regexp) flagLC_ALL=C
Dies dient zur Vermeidung von Problemen in Mehrbyte-Sprachumgebungen, in denengrep
sonst versucht wird, Bytefolgen als Zeichen zu interpretieren.-a
Behandelt Binärdateien, die Textdateien entsprechen (anstelle des normalen Verhaltens, bei demgrep
nur gedruckt wird, ob mindestens eine Übereinstimmung vorliegt oder nicht).quelle
grep
, damit sie übereinstimmen?-a
Option einzuschließen, andernfalls antwortet grep mitBinary file file.bin matches
für jede Datei, die grep als binär erkennt.Was die Eingabedatei (en) als binär behandelt (keine Übersetzung für Zeilenvorschübe oder Codierungen, siehe Perlrun ) , durchläuft dann die Eingabedatei (en) und druckt keinen Zähler für alle Übereinstimmungen des angegebenen Hex (oder in welcher Form auch immer, siehe Perlre ). .
quelle
-0ooo
) verwenden.$/
perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
Mit GNU
awk
können Sie:Wenn es sich bei einem der Bytes um ERE-Operatoren handelt, müssen sie jedoch (mit
\\
) maskiert werden . Wie0x2e
die.
wäre als eingegeben werden\\.
oder\\\x2e
. Ansonsten sollte es mit beliebigen Bytewerten einschließlich 0 und 0xa funktionieren.Beachten Sie, dass dies nicht so einfach ist,
NR-1
da es einige Sonderfälle gibt:RT==""
.Beachten Sie auch, dass im schlimmsten Fall (wenn die Datei keinen Suchbegriff enthält) die Datei vollständig in den Speicher geladen wird.
quelle
Die einfachste Übersetzung, die ich sehe, ist:
Wo ich verwendet habe ,
$'\xef'
als die bash-ANSI zitiert (ursprünglich eineksh93
Funktion, unterstützt jetzt durchzsh
,bash
,mksh
, FreeBSDsh
) Version von Fisch\Xef
und verwendetgrep -o ... | wc -l
die Instanzen zu zählen.grep -o
gibt jede Übereinstimmung in einer separaten Zeile aus. Das-a
Flag bewirkt, dass sich grep bei Binärdateien genauso verhält wie bei Textdateien.-F
ist für feste Zeichenfolgen vorgesehen, damit Sie Regex-Operatoren nicht entziehen müssen.Wie in Ihrem
fish
Fall können Sie diesen Ansatz jedoch nicht verwenden, wenn die zu suchende Sequenz die Bytes 0 oder 0xa (Newline in ASCII) enthält.quelle
printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'
wäre die portabelste "reine Shell" -Methode. Natürlich:printf "efbeadde" | xxd -p -r > hugohex
scheint die praktischste Methode zu sein.Sie können die
bytes.count
Methode von Python verwenden, um die Gesamtzahl der nicht überlappenden Teilzeichenfolgen in einem Bytestring abzurufen.Dieser Einzeiler lädt die gesamte Datei in den Speicher, ist also nicht der effizienteste, funktioniert aber und ist besser lesbar als Perl; D
quelle
239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=
(gd & r)mmap()
eine Datei in Python ; das würde das Memory Commit reduzieren.quelle
Ich denke, Sie können Perl verwenden, probieren Sie es aus:
Ersetzen Befehl
s
gibt Anzahl der Ersetzungen vorgenommen, -0777 Mittel nicht behandeln neue Linie als Sonderzeichen,e
- Befehl ausführen,say
zu drucken , was Zeilenendmarke drucken nächste dann geht,n
hatte ich nicht ganz begriffen, aber nicht funktioniert w / out - von docs:quelle