Из данного файла у меня есть требование создать копию, дополненную нулями до определенного размера.
Если вы создаете файл со следующим.
echo test >testfile
Вывод следующей команды противоречив.
cat testfile /dev/zero | dd bs=256k count=1 status=none | od -c
Это выход, который я ожидал.
0000000 t e s t \n \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
1000000
Но вы также случайно получаете одно из следующего.
0000000 t e s t \n
0000005
0000000 t e s t \n \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000020 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0400000 \0 \0 \0 \0 \0
0400005
Почему эта команда имеет противоречивое поведение?
Даже если dd обрезает канал в конце первого файла, результат 128k странный. Я получаю те же противоречивые результаты в системах 16.04, 18.04 и 19.04.
Вам необходимо указать полные блоки. Попробуйте:
cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | od -c
Из man dd
:
fullblock
blockquote>
& nbsp; & nbsp; & nbsp; & NBSP; & NBSP; & NBSP; накапливать полные блоки ввода (только iflag)Пример
Заметьте, что без
fullblock
число байтов несовместимо:$ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c 5 $ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k count=1 status=none | wc -c 5
с
iflag=fullbock
, я вижу последовательные полные байты:$ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144 $ cat testfile /dev/zero | dd bs=256k iflag=fullblock count=1 status=none | wc -c 262144
Суть проблемы двояка. Одна часть проблемы - короткое или частичное read ()
. Согласно спецификациям POSIX :
Частичный входной блок - это блок, для которого read () вернул меньше размера входного блока.
Это типично для трубок, и именно это и происходит в вопросе. Одно из решений - использовать расширение GNU iflag = fullblock
, и это версия, которую использует Ubuntu. Из GNU dd manual :
Обратите внимание, что если ввод может возвращать короткие чтения, как это могло бы иметь место при чтении из канала, например, 'iflag = fullblock' гарантирует, что 'count =' соответствует завершению входные блоки, а не традиционное поведение, указанное в POSIX, по подсчету операций чтения ввода.
POSIX dd
, MirOS dd
, FreeBSD dd
- у них такой опции нет (хотя были запросы , чтобы добавить это в спецификацию POSIX). Итак, как мы можем писать переносимые сценарии с dd
, которые вы, возможно, захотите перенести из Ubuntu, чтобы сказать FreeBSD? Что ж, отчасти проблема связана с флагом count = 1
. Он сообщает dd
, сколько вызовов read ()
выполнить. Попробуйте выполнить несколько трассировок на dd if = / dev / urandom | strace -e read dd of = / dev / null bs = 256k count = 1
, и вы увидите, что всегда есть только один read ()
, который часто является частичным.(Обратите также внимание: не удивляйтесь, если вы увидите 262144 байта, прочитанные вместо 256000, потому что 256k равно 256 * 1024 = 262144)
Решение состоит в том, чтобы перевернуть параметры, то есть сделать размер блока bs = 1
и count = 256k
. Таким образом мы гарантируем отсутствие частичного чтения и всегда читаем 1 байт, но мы сделаем это 256 тысяч раз. И да, это намного медленнее и займет намного больше времени с данными в диапазоне гигабайт / терабайт. В моих тестах iflag = fullblock
был примерно в 100 раз быстрее (разница между 5 миллисекундами и 700 миллисекундами на 256 Кбайт). Однако преимущество в том, что это переносимо и не должно полагаться на расширение GNU dd
, особенно вы не всегда можете установить GNU dd