Невозможно понять аргументы команды dd

Я довольно хорошо знаком с командой dd, но мне редко приходилось использовать ее самостоятельно. Сегодня мне нужно, но я встречаю поведение, которое кажется действительно странным.

Я хочу создать текстовый файл 100M, каждая строка которого содержит одно слово "тестирование". Это была моя первая попытка:

~$ perl -e 'print "testing\n" while 1' | dd of=X bs=1M count=100
0+100 records in
0+100 records out
561152 bytes (561 kB) copied, 0.00416429 s, 135 MB/s

Хм, это странно. Как насчет других комбинаций?

~$ perl -e 'print "testing\n" while 1' | dd of=X bs=100K count=1K
0+1024 records in
0+1024 records out
4268032 bytes (4.3 MB) copied, 0.0353145 s, 121 MB/s

~$ perl -e 'print "testing\n" while 1' | dd of=X bs=10K count=10K
86+10154 records in
86+10154 records out
42524672 bytes (43 MB) copied, 0.35403 s, 120 MB/s

~$ perl -e 'print "testing\n" while 1' | dd of=X bs=1K count=100K
102400+0 records in
102400+0 records out
104857600 bytes (105 MB) copied, 0.879549 s, 119 MB/s

Итак, из этих четырех явно эквивалентных команд, все производят файлы разных размеров, только один из которых я ожидаю. Почему это?

ИЗМЕНИТЬ: Постепенно я немного смущен, я не думал о "да тестировании" вместо этой более длинной команды Perl.

Ответ 1

Я еще не уверен, почему, но использование этого метода не будет заполнять весь блок перед его сохранением. Попробуйте:

perl -e 'print "testing\n" while 1' | dd of=output.txt bs=10K count=10K iflag=fullblock
10240+0 records in
10240+0 records out
104857600 bytes (105 MB) copied, 2.79572 s, 37.5 MB/s

Кажется, что iflag=fullblock заставляет dd накапливать входные данные до тех пор, пока блок не будет заполнен, хотя я не уверен, почему это не по умолчанию, или то, что оно действительно делает по умолчанию.

Ответ 2

Чтобы узнать, что происходит, рассмотрим вывод strace для аналогичного вызова:

execve("/bin/dd", ["dd", "of=X", "bs=1M", "count=2"], [/* 72 vars */]) = 0
…
read(0, "testing\ntesting\ntesting\ntesting\n"..., 1048576) = 69632
write(1, "testing\ntesting\ntesting\ntesting\n"..., 69632) = 69632
read(0, "testing\ntesting\ntesting\ntesting\n"..., 1048576) = 8192
write(1, "testing\ntesting\ntesting\ntesting\n"..., 8192) = 8192
close(0)                                = 0
close(1)                                = 0
write(2, "0+2 records in\n0+2 records out\n", 31) = 31
write(2, "77824 bytes (78 kB) copied", 26) = 26
write(2, ", 0.000505796 s, 154 MB/s\n", 26) = 26
…

Что происходит, так это то, что dd делает одиночный read() вызов для чтения каждого блока. Это удобно при чтении с ленты, для чего первоначально использовался dd. На лентах read действительно читает блок. При чтении из файла вы должны быть осторожны, чтобы не указывать слишком большой размер блока, иначе read будет усечен. При чтении из трубы это хуже: размер считываемого блока будет зависеть от скорости команды, производящей данные.

Мораль этой истории заключается не в том, чтобы использовать dd для копирования данных, за исключением безопасных небольших блоков. И никогда из трубы, кроме bs=1.

(GNU dd имеет флаг fullblock, чтобы он вел себя прилично, но другие реализации этого не делают.)

Ответ 3

Мое лучшее предположение состоит в том, что dd читает из канала, и когда он пуст, он предполагает, что он читает весь блок. Результаты довольно непоследовательны:

$ perl -e 'print "testing\n" while 1' | dd of=X bs=1M count=100
0+100 records in
0+100 records out
413696 bytes (414 kB) copied, 0.0497362 s, 8.3 MB/s

[email protected] ~
$ perl -e 'print "testing\n" while 1' | dd of=X bs=1M count=100
0+100 records in
0+100 records out
409600 bytes (410 kB) copied, 0.0484852 s, 8.4 MB/s