Заполнение конечных пробелов в строке другим символом

12

Я хотел бы вывести hello worldболее 20 символов.

printf "%-20s :\n\n" 'hello world!!'

# Actual output
hello world!!        :

# Wanted output
hello world!!========:

Тем не менее, я не хочу завершать с пробелами, но вместо " = ". Как мне это сделать?

smarber
источник

Ответы:

9

Обновленный ответ для более общего решения. см. также мой другой ответ ниже, используя только расширение скобки и pritnf.

$ str='Hello World!'
$ sed -r ':loop; s/ (=*):$/\1=:/; t loop' <<< "$(printf '%-20s:\n' "$str" )"
Hello World!========:

Как это устроено?

это (=*):$/захватывает один пробел, один или более =, за которым следует двоеточие :в конце ввода; мы делаем набор =как групповое совпадение и \1будем его обратной ссылкой.

С помощью :loopмы определили названную метку loopи вместе с t loopней перейдем к этой метке, когда a s/ (=*):$/\1=:/выполнит успешную замену;

В замене с помощью \1=:, он всегда будет увеличивать число =s и возвращать само двоеточие до конца строки.

αғsнιη
источник
1
поэтому вы должны настроить флаг в зависимости от того, сколько слов на входе?
user1686
@ Grawity Я обновил свой ответ на общее решение сейчас.
αғsнιη
20
filler='===================='
string='foo'

printf '%s\n' "$string${filler:${#string}}"

дает

foo=================

${#string}длина значения $stringи ${filler:${#string}}подстрока $fillerот смещения ${#string}вперед.

Общая ширина вывода будет равна максимальной ширине $fillerили $string.

Строка-заполнитель может в системах jot, которые создаются динамически с использованием

filler=$( jot -s '' -c 16 '=' '=' )

(по 16 =в строке). Системы GNU могут использовать seq:

filler=$( seq -s '=' 1 16 | tr -dc '=' )

Другие системы могут использовать Perl или какой-либо другой более быстрый способ динамического создания строки.

Кусалананда
источник
почему бы не использовать printfдля генерации фильтра, который почти доступен во всех системах, и расширение скобок с подобными оболочками bash/szh?
αғsнιη
@sddgob Как бы вы сделали это с printfрасширением + скобка bash?
Кусалананда
пожалуйста, проверьте unix.stackexchange.com/a/399059/72456
αғsнιη
17
printf "%.20s:\n\n" "$str========================="

где %.20sформат усечения строки

JJoao
источник
2
Это ИМХО, лучшее решение, чем мое. Требуется только краткое объяснение строки формата.
Кусалананда
12

Один из способов сделать это:

printf "====================:\r%s\n\n" 'hello world!!'
Сату Кацура
источник
6
Ха! Это умный трюк! Тем не менее, он на самом деле будет печатать ====================\rhello world, что может быть проблемой, если оператору нужно сохранить это, а не просто напечатать его на экране.
Тердон
также echo -e '=================\rHello World!!', но имеет ту же проблему, на которую указал @terdon.
αғsнιη
1
@ αғsнιη Только если echoподдерживает -e. printfпочти всегда лучше, чем echoпо многим причинам.
Satō
5

Подход Perl:

$ perl -le '$k="hello world!!"; while(length($k)<20){$k.="=";} print "$k\n"'
hello world!!=======

Или лучше @SatoKatsura отметил в комментариях:

perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Если вам нужно поддерживать многобайтовые символы UTF, используйте:

PERL_UNICODE='AS' perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'

Та же идея в оболочке:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"
Тердон
источник
Вам не нужен цикл perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Однако это (и все другие решения, опубликованные до сих пор) ломаются, если задействованы многобайтовые символы.
Satō
@ SatōKatsura ооо, да, это здорово! Надо было подумать об этом, спасибо. И да, я думал о добавлении отказа от ответственности за возможный сбой в многобайтовых символах UTF, но решил, что в этом контексте это будет ненужным осложнением.
Terdon
Я думаю, что perl6может быть способ сделать это правильно, даже с многобайтовыми символами. Но с другой стороны perl6это раздражает во многих отношениях.
Satō
@ SatōKatsura хорошо, для такой простой вещи достаточно просто установить PERL_UNICODE='AS'. Например: printf '%s' nóóös | perl -nle 'print length($_)'печатает 8 («неправильно»), а printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'печатает 5 («правильно»).
Тердон
5

Другой способ заключается в использовании только printfкоманды и генерировать шаблон символов дополнени сначала Shell Brace Expansion(Вы можете положить конец с номером ≥ Форматирование области вы хотите напечатать в {1..end}) и получить только каждый первый символ этого , %.1sкоторый является =s , а затем напечатать длину только первые 20 символов область этого %.20s. Это своего рода лучший способ иметь повторяющиеся символы / слова вместо их дублирования.

printf '%.20s:\n' "$str$(printf '%.1s' ={1..20})"
Hello World!!=======:

Пояснения:

Обычно, как Brace Expansion , оболочка расширяется {1..20}следующим образом, если мы печатаем их.

printf '%s ' {1..20}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

Таким образом, при добавлении к нему знака равенства ={1..20}оболочка будет расширяться следующим образом.

printf '%s ' ={1..20}
=1 =2 =3 =4 =5 =6 =7 =8 =9 =10 =11 =12 =13 =14 =15 =16 =17 =18 =19 =20 

И printf '%.1s'это означает printf '%WIDE.LENGTH', что мы печатаем только одну ДЛИНУ из тех , что указаны выше, по умолчанию 1 WIDE . так приведут =только s и 20 раз повторились.

Теперь, когда printf '%.20s:\n'мы печатаем только длину 20, $strа если длина $str<20, остальная часть будет =заполняться из сгенерированных s вместо пробелов.

αғsнιη
источник