Как мне извлечь один кусок байтов из файла?

81

На рабочем столе Linux (RHEL4) я хочу извлечь диапазон байтов (обычно менее 1000) из большого файла (> 1 ГБ). Я знаю смещение в файле и размер куска.

Я могу написать для этого код, но есть ли решение для командной строки?

В идеале что-то вроде:

magicprogram --offset 102567 --size 253 < input.binary > output.binary
DanM
источник

Ответы:

121

Попробуйте dd:

dd skip=102567 count=253 if=input.binary of=output.binary bs=1
Томас Падрон-Маккарти
источник
2
Необязательно добавить, status=noneчтобы подавить вывод в stderr.
kenorb
13
Вот пример с использованием шестигранных смещения: dd if=in.bin bs=1 status=none skip=$((0x88)) count=$((0x80)) of=out.bin.
kenorb
@kenorb: я считаю, что шестнадцатеричный синтаксис является частью Bash, поэтому он не обязательно работает с другими оболочками. Я сам использую tcsh (не бейте меня!), И ваш пример там не работает.
Томас Падрон-Маккарти
1
Есть ли конкретная причина, по которой вы используете bs = 1 и count = 253, а не наоборот? Сделает ли больший размер блока команду более эффективной?
rexford
1
@rexford: номер пропуска также дается блоками и не кратен 253. И, учитывая, что ОС выполняет собственную буферизацию при чтении из обычного файла в файловой системе, в этом случае эффективность не будет такой низкой, как при чтении с устройства.
Thomas Padron-McCarthy
55

Это старый вопрос, но я хотел бы добавить еще одну версию ddкоманды, которая лучше подходит для больших блоков байтов:

dd if=input.binary of=output.binary skip=$offset count=$bytes iflag=skip_bytes,count_bytes 

где $offsetи $bytes- числа в байтовых единицах.

Разница с принятым ответом Томаса заключается в том, bs=1что здесь его нет. bs=1производит размер входного и выходного блока равным 1 байту, что делает его ужасно медленным, когда количество байтов для извлечения велико.

ChronoTrigger
источник
4
Это действительно намного быстрее, чем мой ответ.
Томас Падрон-Маккарти
1
Не работает на Mac - iflagэто неизвестный операнд, и без него вы получите целый блок.
Timmmm
1
@Timmmm GNU ddможно использовать для iflagподдержки ( brew install coreutils). Примечание: по умолчанию утилиты устанавливаются с gпрефиксом (например, gddвместо dd)
Shakil
идеальный трюк для ускорения, я собирался разделить файл размером 48 ГБ, и это спасло мне жизнь
Али Надализаде
11

head -c + tail -c

Не уверен, как это сравнить по ddэффективности, но это весело:

printf "123456789" | tail -c+2 | head -c3

выбирает 3 байта, начиная со второго:

234

См. Также: https://stackoverflow.com/a/1272995/895245

Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
@ elvis.dukaj да, по-другому быть не должно. Просто попробуйте printf '\x01\x02' > fи hd.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
2
Намного быстрее, чем dd с bs = 1, спасибо! Обратите внимание, что tail подсчитывает байты от 1, а не от 0. Кроме того, tail завершается с кодом ошибки 1, когда его вывод преждевременно закрывается головкой. Обязательно игнорируйте эту ошибку при использовании "set -e".
proski
2

Все это может делать команда dd. Посмотрите на параметры поиска и / или пропуска как часть вызова.

Джо
источник
2

Даже быстрее

dd bs=<req len> count=1 skip=<req offset> if=input.binary of=output.binary 
Альберт Бурбеа
источник
2
Проблема здесь в том, что skipэто единицы bs.
Arkku
однако этот ответ должен быть самым популярным, ответ выше с bs = 1 очень медленный: D
Чакабам
это деталь для исполнителя, и все же лучше, чем выше, правда, вам нужно будет пересчитать, например: req_offset=$(bc <<< "$offset/$bs")и убедиться, что получается круглое значение.
Чакабам