Beste Weg, um Bytes vom Anfang einer Datei zu entfernen?

61

Heute musste ich die ersten 1131 Bytes aus einer 800 MB großen gemischten Text- / Binärdatei entfernen, einem gefilterten Subversion-Dump, den ich für ein neues Repository hacke. Wie geht das am besten?

Zunächst habe ich versucht

dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump

Nach dem Überspringen wird jedoch der Rest der Datei jeweils byteweise kopiert, dh sehr langsam. Am Ende habe ich herausgefunden, dass ich 405 Bytes benötigte, um dies auf drei 512er Blöcke aufzurunden, die ich überspringen konnte

dd if=/dev/zero of=405zeros bs=1 count=405
cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump

Was ist ziemlich schnell erledigt, aber es muss einen einfacheren / besseren Weg gegeben haben? Gibt es ein anderes Tool, das ich vergessen habe? Vielen Dank!

Rup
quelle
ddist das richtige Werkzeug für den Job - anscheinend haben Sie eine schöne, elegante Lösung für Ihr Problem gefunden.
Justin Ethier

Antworten:

62

Sie können bs wechseln und Optionen überspringen:

dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump

Auf diese Weise kann die Operation von einem größeren Block profitieren.

Andernfalls könnten Sie es mit tail versuchen (obwohl es nicht sicher ist, es mit Binärdateien zu verwenden):

tail -c +1132 filtered.dump >trimmed.dump

Schließlich können Sie 3 dd-Instanzen verwenden, um so etwas zu schreiben:

dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; }

wo die erste dd druckt seine Standardausgabe gefiltert.dump; der zweite liest gerade 1131 Bytes und wirft sie weg; Dann liest der letzte die verbleibenden Bytes von filters.dump von seiner Standardeingabe und schreibt sie in trimmed.dump.

Marco
quelle
6
Vielen Dank! Ich wusste nicht, dass die Pipe-Eingabe auf einen zweiten Prozess wie diesen übertragen wurde - das ist sehr ordentlich. Ich kann nicht glauben, dass ich nicht daran gedacht habe bs=1131 skip=1: - /
Rup
2
Die meisten modernen Implementierungen von Shell-Dienstprogrammen funktionieren ordnungsgemäß mit Binärdateien (dh sie haben keine Probleme mit Nullzeichen und fügen am Ende der Datei keine zusätzlichen Zeilenumbrüche ein). Sicher sind GNU- und * BSD-Implementierungen sicher.
Gilles 'SO- hör auf böse zu sein'
17

Nicht sicher, wann skip_byteshinzugefügt wurde, aber um die ersten 11 Bytes zu überspringen, haben Sie:

# echo {123456789}-abcdefgh- | 
                              dd bs=4096 skip=11 iflag=skip_bytes
-abcdefgh-
0+1 records in
0+1 records out
11 bytes (11 B) copied, 6.963e-05 s, 158 kB/s

Wobei iflag=skip_bytesdd angewiesen wird, den Wert für die skipOption als Bytes anstelle von Blöcken zu interpretieren , um dies unkompliziert zu gestalten.

Ярослав Рахматуллин
quelle
Mit Sicherheit ein Geschwindigkeitsvorteil für große Dateien und kleine zu entfernende Datenmengen.
29.
Dies ist die beste Antwort, da sie für jede Blockgröße funktioniert, z. B.iflag=skip_bytes skip=1234 bs=1M
phiresky
15

Sie können eine Sub-Shell und zwei ddAufrufe wie folgt verwenden:

$ ( dd bs=1131 count=1 of=dev_null && dd bs=4K of=out.mp3 ) < 100827_MR029_LobbyControl.mp3
1+0 records in
1+0 records out
1131 bytes (1.1 kB) copied, 7.9691e-05 s, 14.2 MB/s
22433+1 records in
22433+1 records out
91886130 bytes (92 MB) copied, 0.329823 s, 279 MB/s
$ ls -l *
-rw------- 1 max users 91887261 2011-02-03 22:59 100827_MR029_LobbyControl.mp3
-rw-r--r-- 1 max users     1131 2011-02-03 23:04 dev_null
-rw-r--r-- 1 max users 91886130 2011-02-03 23:04 out.mp3
$ cat dev_null out.mp3 > orig
$ cmp 100827_MR029_LobbyControl.mp3 orig
maxschlepzig
quelle
1
Danke - Ich wusste nicht, dass die Pipe-Eingabe mit einem zweiten Prozess wie diesem fortgesetzt wird. Ich denke, das ist die Sub-Shell? Ich werde mich definitiv daran erinnern! Ich habe Marco das Häkchen gegeben, weil er hier zuerst aber +1 bekam und danke für die Antwort!
Rup
1
@Rup, ja, die über die Klammern erzeugte Sub-Shell liefert einen stdin-Dateideskriptor, und beide dd-Aufrufe verbrauchen nacheinander die Eingabe von ihr. Ja - Marco hat mich um 29 Sekunden geschlagen :)
maxschlepzig
6

Wenn das Dateisystem und der Linux-Kernel dies unterstützen, können Sie versuchen fallocate, die Änderungen zu übernehmen: Im besten Fall gibt es überhaupt keine Daten-E / A:

$ fallocate <magic> -o 0 -l 1131 inplace.dump

Wo <magic>hängt vom Dateisystem, der Linux-Version und dem Dateityp ab ( FALLOC_FL_COLLAPSE_RANGEoder FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEkann intern verwendet werden ).

jfs
quelle
1
Dies ist meine bevorzugte Methode, aber die Ausführung in einem Container hat Probleme. stackoverflow.com/questions/31155591/…
Michaelcurry
3

Sie sollten verwenden count=0- das ist einfach, lseek()wann immer es möglich ist.

So was:

{  dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump

ddwird lseek()der Eingang Dateideskriptor zu einem 1131 - Byte - Offset, und dann catwird einfach kopieren , was zur Ausgabe bleibt.

mikeserv
quelle
2

Ein weiterer Weg führt Bytes aus der Datei zu entfernen (ohne die Verwendung von ddüberhaupt) zu verwenden ist, xxdund sedoder tailist.

bytes=$((1131*2))

xxd -p -c 256 filtered.dump | tr -d '\n' | sed "s/^.\{0,${bytes}\}//" | xxd -r -p > trimmed.dump

bytes=$((bytes + 1)) 
xxd -p -c 256 filtered.dump | tr -d '\n' | tail -c +${bytes} | xxd -r -p > trimmed.dump
wop
quelle
Das ist ordentlich, aber ich glaube, ich arbeite lieber mit der Datei in Binärform als sie in und aus Hex zu konvertieren.
Rup
2

@maxschlepzig fragt nach einem Online-Liner. Hier ist einer in Perl. Es braucht 2 Argumente: Von Byte und Länge. Die Eingabedatei muss mit '<' angegeben werden und die Ausgabe erfolgt auf stdout:

perl -e 'sysseek(STDIN,shift,0) || die; $left = shift;
     while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){
        $left -= $read; syswrite(STDOUT,$buf);
     }' 12345678901 19876543212 < bigfile > outfile

Ist die Länge größer als die Datei, wird der Rest der Datei kopiert.

Auf meinem System liefert dies 3,5 GB / s.

Ole Tange
quelle
Ich denke, seine einzeilige Herausforderung bestand darin, Sie dazu zu bringen, zu beweisen, dass die Skriptsprachenlösung besser war als seine einzeilige Shell-Lösung. Und ich bevorzuge seins: es ist kürzer und klarer zu mir. Wenn Ihre Leistung besser ist, liegt das daran, dass Sie einen größeren Block als ihn verwenden, was sich auch in seiner Version leicht verbessern lässt.
Rup
@ Rup Ach, aber nein. Sie scheinen zu vergessen, dass ddein vollständiger Lesevorgang nicht garantiert. Versuchen Sie: ja | dd bs = 1024k count = 10 | wc unix.stackexchange.com/questions/17295/…
Ole Tange
Auch meine Lösung liest nicht die Bytes, die Sie nicht benötigen (die mehrere Terabyte lang sein können).
Ole Tange