Требуется объяснение того, как я могу повторить символ в оболочке POSIX

8

Следующий ответ на переполнение стека,

Как я могу повторить символ в Bash?

навязывает один правдоподобный способ POSIX - только повторение одного символа следующим образом. В этом примере давайте используем знак равенства 100 раз:

printf %100s | tr " " "="

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

LinuxSecurityFreak
источник

Ответы:

13

Короче говоря, printf %100sнапечатает 100 пробелов и tr " " "="преобразует эти пробелы в знаки равенства, эффективно печатая 100 знаков равенства.

Разбивая это:


printfэто встроенная оболочка Обычно требуется два или более аргумента, первый из которых является «строкой формата», а остальные будут использоваться для заполнения заполнителей в этой строке формата. Как только этот шаблон будет полностью заполнен, он распечатает результат. Если осталось больше аргументов, он начнется заново, заполняя больше аргументов и печатая полученную строку.

Строка формата, используемая для, printfпринимает спецификации формата, которые начинаются %и заканчиваются одной буквой, поэтому %dозначает целое число (с использованием десятичной основы, следовательно, «d»), %fозначает число с плавающей запятой и %sозначает строку символов. Символы, отличные от букв после, %являются модификаторами для спецификации формата, и, в частности, цифры используются для указания запрошенной длины поля на выходе. Таким образом %100s, строка будет иметь формат не менее 100 символов, заполнит ее пробелами и выровнит ее по правому краю (другими словами, добавит пробелы в начале строки).

Если передан дополнительный аргумент, он будет использовать его для этого %sполя, поэтому, например printf %100s abc, напечатает 97 пробелов (чтобы получить общее число 100, учитывая 3 в «abc»), за которыми следует фактическая строка «abc». Но если аргумент не указан, то спецификация формата заполняется пустым или нулевым аргументом (для которого пустая строка, для %s0 будет 0 %dи т. Д.). То же самое, как если бы была передана пустая строка, например: printf %100s '', Конечным результатом является то, что печатается только заполнение из 100 символов.

Таким образом, все это printf %100sприводит к печати 100 пробелов.


Теперь trэто инструмент для перевода символов из ввода в вывод. Он принимает два аргумента, SET1 и SET2, каждый набор символов, а затем переводит первый символ SET1 в первый из SET2, второй символ SET1 во второй из SET2 и так далее. trчитает входные данные из stdin и записывает их обратно в stdout (так что это очень полезно в конвейерах, подобных приведенному выше.) trвсегда переводит все вхождения этого символа в данную строку.

Например, tr aeiou 12345будет переводить строчные гласные в числа от 1 до 5 в этом порядке, поэтому он будет переводить «очередь» в «q52523ng», например. Вы также можете передать ему диапазон символов, например, tr a-z A-Zчтобы превратить любую строчную букву в соответствующую заглавную.

Так tr " " "="же просто переводить пробелы в знаки равенства по всей строке. Первое место должно быть заключено в кавычки, чтобы быть признанным в качестве аргумента. На =самом деле не нужно цитировать, но это не повредит. tr " " =работал бы так же.


Собрав все воедино, выведите 100 пробелов, а затем переведите каждый из них в знаки равенства.

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

filbranden
источник
Просто проверяю, будет ли следующее синтаксически правильнее ?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
2
@Vlastimil На самом деле printf '%100s' '', с пустой строкой ... Я обновил ответ, чтобы включить это. В этом конкретном случае пустая строка или один пробел не будут иметь значения, но вы можете увидеть разницу printf '%sx\n', которая такая же, printf '%sx\n' ''но отличается от printf '%sx\n' ' '. Надеюсь, это поможет!
filbranden
1
+1 за упоминание, что trдействует на наборы символов. Это часто не учитывается.
Сергей Колодяжный
11

Команда printfиспользует свой первый аргумент в качестве формата для печати своих последующих аргументов. printf %100sраспечатывает аргументы шириной до 100 символов, используя пробелы (слева). Для форматирования нет аргументов, поэтому он форматирует пустую строку один раз и выводит 100 пробелов. Ты это видишь:

$ printf %100s | hexdump -C
00000000  20 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  |                |
*
00000064

(20 - гекс для пробела; *означает повторение предыдущей строки)

В строках формата приблизительно используются Xprintfспецификаторы C : %необязательная ширина для соответствия форматированному значению и тип используемого формата. sэто форматирование строки, и строки дополняются пробелами слева по умолчанию. Может быть несколько форматов или другие буквенные части: printf "a%10sb\n" helloпечатные издания

 a         xb.

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

$ tr " " "="
hello world
hello=world

(Я напечатал "Привет, мир")

Вы могли бы иметь несколько замен: tr abc defпревращает a в d, b в e, c в f, а остальное оставляет без изменений. Здесь это всего лишь один символ, так как это было то, что printfмогло бы дешево генерировать.

Канал |приводит к выводу команды слева, printf %100sкоторая будет использоваться в качестве ввода для команды справа tr " " "=",. Таким образом, дается сотня последовательных пробелов tr, и каждый из них заменяется на =, с новой распечатанной строкой.

printf %100s | tr " " "="
====================================================================================================
Майкл Гомер
источник
Просто проверяю, будет ли следующее синтаксически правильнее ?:printf '%100s' ' ' | tr " " "="
LinuxSecurityFreak
1
Форматирование пробела и заполнение его пробелами даст тот же результат, что и форматирование пустой строки и заполнение пробелами, но это не является структурно эквивалентным. Возможно, это более «правильно» с точки зрения ясности того, что происходит, но не синтаксически, и это разные команды.
Майкл Гомер