it-swarm.com.de

Der beste Weg, um Bytes vom Anfang einer Datei zu entfernen?

Heute musste ich die ersten 1131 Bytes aus einer 800 MB gemischten Text-/Binärdatei entfernen, einem gefilterten Subversion-Dump, den ich für ein neues Repository hacke. Was ist der beste Weg, dies zu tun?

Zunächst habe ich es versucht

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

nach dem Überspringen wird jedoch der Rest der Datei byteweise kopiert, d. h. sehr langsam. Am Ende habe ich herausgefunden, dass ich 405 Bytes brauchte, um dies auf drei 512-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 war ziemlich schnell erledigt, aber es muss einen einfacheren/besseren Weg gegeben haben? Gibt es ein anderes Tool, das ich vergessen habe? Vielen Dank!

64
Rup

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 Folgendes zu schreiben:

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

wobei der erste dd seine Standardausgabe filtered.dump druckt; der zweite liest nur 1131 Bytes und wirft sie weg; Dann liest der letzte von seiner Standardeingabe die verbleibenden Bytes von filtered.dump und schreibt sie in trimmed.dump.

67
marco

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

# 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

Wo iflag=skip_bytes weist dd an, den Wert für die Option skip als Bytes anstelle von Blöcken zu interpretieren, was ihn unkompliziert macht.

Sie können eine Sub-Shell und zwei dd Aufrufe 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
15
maxschlepzig

Wenn das Dateisystem und der Linux-Kernel dies unterstützen, können Sie fallocate versuchen, wenn Sie die Änderungen an Ort und Stelle vornehmen möchten: Im besten Fall gibt es keine Daten IO überhaupt:

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

wo <magic> hängt vom Dateisystem, der Linux-Version und dem Dateityp ab ( FALLOC_FL_COLLAPSE_RANGE oder FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE könnte intern verwendet werden ).

7
jfs

Sie sollten count=0 Verwenden - das ist eine einfache lseek(), wann immer dies möglich ist.

So was:

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

dd wird lseek() den Deskriptor der Eingabedatei auf einen Versatz von 1131 Byte setzen, und dann wird cat einfach alles kopieren, was zur Ausgabe übrig bleibt.

3
mikeserv

Eine weitere Möglichkeit, führende Bytes aus einer Datei zu entfernen (ohne dd zu verwenden), besteht darin, xxd und sed bzw. tail zu verwenden.

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
2
wop

@maxschlepzig fragt nach einem Online-Liner. Hier ist einer in Perl. Es dauert 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

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

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

2
Ole Tange