Написание символа N раз с использованием команды printf

12

Я нашел следующую команду, чтобы повторить символ в Linux:

printf 'H%.0s' {1..5000} > H.txt

Я хочу, например, Hповторить 5000раз. Что %.0sзначит здесь?

звезда
источник
С tcshили zsh, repeat 5000 printf Hпроще понять. С perl: print "H" x 5000(обратите внимание, что {1..5000}это оператор zsh, вдохновленный оператором perls, 1..5000а затем скопированным ksh93 и bash)
Стефан Шазелас
да, это работает, но использует много ресурсов для большего повторения, следуйте советам Стефана
Шазеласа
1
я бы сделал эту командуyes H|head -5000|tr -d '\012'
Skaperen
dd if=/dev/zero bs=5000 count=1 | tr '\0' H
Кодзиро
@Skaepren:yes H| head -n 2500| tr \\n H
mikeserv

Ответы:

20

Эта команда зависит от оболочки, генерирующей 5000 аргументов и передающей их, printfкоторая затем игнорирует их. Хотя это может показаться довольно быстрым - и относительно некоторых вещей - оболочка все равно должна генерировать все эти строки как аргументы (и разделять их) и так далее.

Помимо того факта, что сгенерированные H не могут быть напечатаны до тех пор, пока оболочка не выполнит итерацию до 5000, эта команда также стоит в памяти все, что требуется для хранения и ограничения числовых строковых аргументов printf плюс Hs. Так же просто, как вы можете сделать:

printf %05000s|tr \  H

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

Я побежал ...

time bash -c 'printf H%.0s {1..5000}' >/dev/null

...и...

time bash -c 'printf %05000s|tr \  H' >/dev/null

Каждый примерно 5 раз за штуку (ничего научного здесь - только анекдотичный), и версия расширения фигурной скобки в среднем trзанимала чуть более 0,02 секунды в общем времени обработки, но версия в среднем занимала около 0,012 секунды - и trверсия побеждала каждый раз. Я не могу сказать, что удивлен - {brace expansion}это полезная функция сокращенной интерактивной оболочки, но обычно она довольно расточительна, когда речь идет о любом виде сценариев. Распространенная форма:

for i in {[num]..[num]}; do ...

... когда вы думаете об этом, на самом деле это два for цикла - первый является внутренним и подразумевает, что оболочка должна каким-то образом выполнить цикл для генерации этих итераторов, прежде чем сохранять их все и повторять их снова для вашего forцикла. Такие вещи обычно лучше сделать как:

iterator=$start
until [ "$((iterator+=interval))" -gt "$end" ]; do ...

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

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

printf %05000d

Я делаю оба без аргументов, потому что для каждого аргумента, указанного в printfстроке формата, когда аргумент не найден, используется пустая строка, которая интерпретируется как ноль для аргумента с цифрой или пустая строка для строки.

Это другая (и, на мой взгляд, более эффективная) сторона медали по сравнению с командой, о которой идет речь, - хотя можно получить что-то от чего-либо, как вы, когда вы printf %.0длина строк для каждого аргумента, также можно получить что-то из ничего.

Еще быстрее для большого количества сгенерированных байтов, которые вы можете использовать ddкак:

printf \\0| dd bs=64k conv=sync 

... и аргумент ddс обычными файлами seek=[num]может быть использован для большего преимущества. Вы можете получить 64 тыс. Новых строк, а не нулей, если добавите ,unblock cbs=1к вышеупомянутому и оттуда можете вставлять произвольные строки в строку с помощью pasteи /dev/null- но в этом случае, если это доступно для вас, вы также можете использовать:

yes 'output string forever'

Вот еще несколько ddпримеров:

dd bs=5000 seek=1 if=/dev/null of=./H.txt

... который создает (или усекает) в \0NULзаполненный файл в текущем каталоге с именем H.txt от размера 5000 байт. ddстремится прямо к смещению и заполняет все NUL за ним.

<&1 dd bs=5000 conv=sync,noerror count=1 | tr \\0 H >./H.txt

... который создает файл с тем же именем и размером, но заполненный символами W / H. Он использует преимущества ddспецифицированного поведения по записи по крайней мере одного полного нулевого блока в случае ошибки чтения, когда указаны noerrorи syncпреобразования (и - без count=- скорее всего, будет длиться дольше, чем вы хотели) , и намеренно перенаправляет файловый дескриптор, доступный только для записи, в ddstdin.

mikeserv
источник
8

%.0sОзначает преобразовать аргумент как строку , с точностью нуля. Согласно man 3 printf, значение точности в таком случае дает

   [ ... ] the  maximum  number  of characters to be printed from a
   string for s and S conversions.

следовательно, когда точность равна нулю, строковый аргумент вообще не печатается. Однако H(который является частью спецификатора формата) печатается столько раз, сколько есть аргументов, поскольку согласно printfразделуman bash

The format is reused as necessary to consume all  of  the  argu
ments.  If the format requires more arguments than are supplied,
the extra format specifications behave as if  a  zero  value  or
null  string,  as  appropriate,  had  been supplied. 
steeldriver
источник
7

В этом случае %.0sвсегда печатается один экземпляр символа (ов), предшествующий ему, в этом случае H. Когда вы используете {1..5000}, оболочка расширяет его и становится:

printf 'H%.0s' 1 2 3 4 ... 5000 > H.txt

то есть команда printf теперь имеет 5000 аргументов, и для каждого аргумента вы получите один H. Они не должны быть последовательными или числовыми:

printf 'H%.0s' a bc fg 12 34

печатает HHHHH- т.е. количество аргументов, 5 в этом случае.

Обратите внимание, что эллипсы в 1-м примере выше не вставляются буквально, они там указывают последовательность или диапазон.

KM.
источник