it-swarm-eu.dev

Nejlepší způsob, jak odstranit bajty od začátku souboru?

Dnes jsem musel odstranit prvních 1131 bajtů ze 800 MB smíšeného textového/binárního souboru, filtrovaného výpisu Subversion, který hackuji do nového úložiště. Jaký je nejlepší způsob, jak toho dosáhnout?

Nejprve jsem to zkusil

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

ale po přeskočení to zkopíruje zbytek souboru bajt najednou, tj. velmi pomalu. Nakonec jsem vypracoval, že jsem potřeboval 405 bytů, abych to zaokrouhlil na tři bloky po 512, které bych mohl přeskočit

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

která skončila poměrně rychle, ale muselo existovat jednodušší/lepší způsob? Je tu další nástroj, na který jsem zapomněl? Dík!

64
Rup

Můžete přepínat možnosti bs a přeskočit:

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

Tímto způsobem může operace těžit z většího bloku.

V opačném případě můžete zkusit s ocasem (ačkoli není bezpečné používat jej s binárními soubory):

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

Nakonec můžete použít 3 dd instance k napsání něčeho takového:

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

kde první dd vytiskne svůj standardní výstup filtrovaný.dump; druhý právě přečte 1131 bytů a zahodí je; potom poslední přečte ze svého standardního vstupu zbývající bajty filtrovaného.dumpu a zapíše je do oříznutého.dumpu.

67
marco

Nejste si jisti, kdy skip_bytes byl přidán, ale přeskočit prvních 11 bajtů máte:

# 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

Kde iflag=skip_bytes řekne dd, aby interpretoval hodnotu volby skip jako bajty namísto bloků, což je jednoduché.

Můžete použít subshell a dva dd hovory takto:

$ ( 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

Pokud to souborový systém a linuxové jádro podporují, můžete zkusit fallocate , pokud chcete provést změny na místě: v nejlepším případě neexistují žádná data IO vůbec:

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

kde <magic> záleží na systému souborů, verzi systému Linux a typu souboru ( FALLOC_FL_COLLAPSE_RANGE nebo FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE lze použít interně ).

7
jfs

Měli byste použít count=0 - to je jednoduchá lseek(), kdykoli je to možné.

Takhle:

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

dd bude lseek() deskriptor vstupního souboru s posunem 1131 bajtů a potom cat jednoduše zkopíruje zbývající část, která zbývá k výstupu.

3
mikeserv

Dalším způsobem, jak odstranit vedoucí bajty ze souboru (bez použití dd vůbec), je použití xxd a sed respektive tail.

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 žádá o online linii. Tady je jeden v Perlu. Trvá to 2 argumenty: Z bajtu a délky. Vstupní soubor musí být zadán jako '<' a výstup bude na výstupu:

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

Pokud je délka větší než soubor, zkopíruje se zbytek souboru.

V mém systému to přináší 3,5 GB/s.

2
Ole Tange