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


62

Сегодня мне пришлось удалить первые 1131 байт из смешанного текстового / двоичного файла размером 800 МБ, отфильтрованного дампа подрывной деятельности, который я взламываю для нового хранилища. Какой лучший способ сделать это?

Для начала я попробовал

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

но после пропуска это копирует оставшуюся часть файла байт за раз, то есть очень медленно. В итоге я решил, что мне нужно 405 байт, чтобы округлить до трех блоков из 512, которые я мог пропустить

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

который завершился довольно быстро, но, должно быть, был более простой / лучший способ? Есть ли другой инструмент, о котором я забыл? Спасибо!


ddэто правильный инструмент для работы - похоже, вы придумали хорошее и элегантное решение вашей проблемы.
Джастин Этье

Ответы:


64

Вы можете переключать bs и пропускать опции:

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

Таким образом, операция может выиграть от большего блока.

В противном случае вы можете попробовать использовать tail (хотя использовать его с двоичными файлами небезопасно):

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

Наконец, вы можете использовать 3 экземпляра dd, чтобы написать что-то вроде этого:

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

где первый дд выводит свой стандартный вывод filter.dump; второй просто читает 1131 байт и выбрасывает их; затем последний читает из своего стандартного ввода оставшиеся байты фильтрованного файла Filter.dump и записывает их в trimmed.dump.


6
Спасибо! Я не знал, что ввод по каналу перенесен на второй процесс, как этот - это очень аккуратно. Я не могу поверить, что я не думал, bs=1131 skip=1хотя: - /
Rup

2
Большинство современных реализаций утилит оболочки работают корректно с двоичными файлами (то есть они не имеют проблем с нулевыми символами и не будут вставлять дополнительную новую строку в конце файла). Конечно, реализации GNU и * BSD безопасны.
Жиль "ТАК - перестань быть злым"

Что означает «небезопасно использовать его с двоичными файлами»?
Скотт

18

Не уверен, когда skip_bytesбыл добавлен, но чтобы пропустить первые 11 байтов, у вас есть:

# 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

Где iflag=skip_bytesуказывает dd интерпретировать значение skipпараметра как байты вместо блоков, что делает его простым.


Конечно, преимущество в скорости для больших файлов и небольшого объема данных, которые будут удалены.
sstn

Это лучший ответ, так как он работает для каждого размера блока, напримерiflag=skip_bytes skip=1234 bs=1M
phiresky

15

Вы можете использовать вложенную оболочку и два ddвызова, например:

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

1
Спасибо - я не знал, что ввод по каналу продолжался до второго процесса, как я полагаю, это вспомогательная оболочка? Я точно запомню это! Я поставил галочку на Марко, потому что он пришел первым, но +1, и спасибо за ответ!
Rup

1
@Rup, да, под-оболочка, созданная в скобках, предоставляет дескриптор файла stdin, и оба вызова dd последовательно потребляют входные данные из него. Да - Марко победил меня на 29 секунд :)
maxschlepzig

6

Если файловая система и ядро ​​Linux поддерживают ее, то вы можете попробовать, fallocateесли хотите внести изменения на месте: в лучшем случае ввода-вывода данных вообще нет:

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

где <magic>зависит от файловой системы, версии Linux и типа файла ( FALLOC_FL_COLLAPSE_RANGEили FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZEможет использоваться внутри ).


1
Это мой предпочтительный метод, но его запуск в контейнере имеет свои проблемы. stackoverflow.com/questions/31155591/…
michaelcurry

3

Вы должны использовать count=0- это просто, lseek()когда это возможно.

Нравится:

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

ddбудет lseek()дескриптор входного файла со смещением 1131 байт, а затем catпросто скопирует все, что осталось для вывода.


2

Еще один способ удалить начальные байты из файла ( 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

Это здорово, но я думаю, что я предпочитаю просто работать с файлом в двоичном формате, а не преобразовывать его в и из шестнадцатеричного.
Rup

2

@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

Если длина больше, чем файл, остальная часть файла будет скопирована.

На моей системе это обеспечивает 3,5 ГБ / с.


Я думаю, что его однострочная задача состояла в том, чтобы заставить вас доказать, что решение на языке сценариев было лучше, чем его решение на одной строке. И я предпочитаю его: он короче и понятнее для меня. Если ваш работает лучше, это потому, что вы используете больший размер блока, чем он, что также легко поднимается в его версии.
Rup

@Rup Увы, но нет. Вы, кажется, забыли, что ddне гарантирует полное чтение. Попробуйте: да | дд бс = 1024к кол = 10 | wc unix.stackexchange.com/questions/17295/…
Оле

Также мое решение не будет считывать ненужные вам байты (которые могут быть длиной в несколько терабайт).
Оле Танге
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.