лучший способ удалить байты из начала файла?

<p>сегодня мне пришлось удалить первые 1131 байт из смешанного текстового/двоичного файла размером 800 мб, отфильтрованного дампа subversion, который я взламываю для нового репозитория. какой лучший способ сделать это?</p><p>для начала я попробовал</p><pre><code>dd bs=1 skip=1131 if=filtered.dump of=trimmed.dump </code></pre><p>но после пропуска это копирует остаток файла по байтам, то есть очень медленно. в итоге я решил, что мне нужно 405 байт, чтобы округлить до трех блоков из 512, которые я мог пропустить</p><pre><code>dd if=/dev/zero of=405zeros bs=1 count=405 cat 405zeros filtered.dump | dd bs=512 skip=3 of=trimmed.dump </code></pre><p>который завершился довольно быстро, но, должно быть, был более простой/лучший способ? есть ли другой инструмент, о котором я забыл? спасибо!</p>

<p>вы можете переключать bs и пропускать опции:</p><pre><code>dd bs=1131 skip=1 if=filtered.dump of=trimmed.dump </code></pre><p>таким образом, операция может выиграть от большего блока.</p><p>в противном случае вы можете попробовать использовать tail (хотя использовать его с двоичными файлами небезопасно):</p><pre><code>tail -c +1132 filtered.dump >trimmed.dump </code></pre><p>наконец, вы можете использовать 3 экземпляра dd, чтобы написать что-то вроде этого:</p><pre><code>dd if=filtered.dump bs=512k | { dd bs=1131 count=1 of=/dev/null; dd bs=512k of=trimmed.dump; } </code></pre><p>где первый дд выводит свой стандартный вывод filter.dump; второй просто читает 1131 байт и выбрасывает их; затем последний читает из своего стандартного ввода оставшиеся байты фильтрованного файла filter.dump и записывает их в trimmed.dump.</p>

<p>не уверен, когда <code>skip\_bytes</code> был добавлен, но чтобы пропустить первые 11 байтов, у вас есть:</p><pre><code># 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 </code></pre><p>куда <code>iflag=skip\_bytes</code> говорит dd интерпретировать значение параметра <code>skip</code> как байты вместо блоков, что делает его простым.</p>

<p>вы можете использовать вложенную оболочку и два вызова <code>dd</code>, например:</p><pre><code>$ ( 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 </code></pre>

<p>если файловая система и ядро linux поддерживают ее, вы можете попробовать <a href="http://man7.org/linux/man-pages/man1/fallocate.1.html"><code>fallocate</code></a> , если хотите внести изменения на месте: в лучшем случае нет данных io на всех:</p><pre><code>$ fallocate <magic> -o 0 -l 1131 inplace.dump где <magic> зависит от файловой системы, версии linux и типа файла ( <a href="https://github.com/karelzak/util-linux/blob/4310faf9507b863adf0d735564f28db5c68ca600/sys-utils/fallocate.c#l303">falloc_fl_collapse_range или falloc_fl_punch_hole | falloc_fl_keep_size может использоваться внутри ).

вы должны использовать _count=0_ - это просто lseek() всякий раз, когда это возможно.нравится:_{ dd bs=1131 skip=1 count=0; cat; } <filtered.dump >trimmed.dump _dd будет lseek() дескриптор входного файла со смещением 1131 байт, а затем cat просто скопирует все, что осталось для вывода.

еще один способ удалить начальные байты из файла (вообще не используя dd) - это использовать xxd и sed или 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 

@maxschlepzig просит онлайн лайнера. вот один в perl. требуется 2 аргумента: от байта и длины. входной файл должен быть задан как '<', а вывод будет на 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 </code></pre><p>если длина больше, чем файл, остальная часть файла будет скопирована.</p><p>на моей системе это обеспечивает 3,5 гб/с.</p>