Wypełnianie końcowych białych znaków w ciągu innym znakiem

12

Chciałbym wydrukować hello worldponad 20 znaków.

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

# Actual output
hello world!!        :

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

Nie chcę jednak uzupełniać spacjami, ale zamiast „ = ”. Jak mogę to zrobić?

smarber
źródło

Odpowiedzi:

9

Zaktualizowana odpowiedź, aby być bardziej ogólnym rozwiązaniem. zobacz także moją inną odpowiedź poniżej, używając tylko rozszerzenia nawiasów klamrowych i pritnf.

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

Jak to działa?

to (=*):$/przechwytuje jedną spację, jedną lub więcej =, po której następuje dwukropek :na końcu wejścia; robimy zestaw =jako dopasowanie grupowe i \1będzie jego odniesieniem wstecznym.

Z :loopzdefiniowaliśmy etykietę o nazwie, loopa wraz z t loopnią przeskoczy do tej etykiety, gdy s/ (=*):$/\1=:/zakończy się powodzeniem;

W części zastępującej \1=:zawsze będzie zwiększać liczbę =si cofać dwukropek do końca łańcucha.

αғsнιη
źródło
1
więc musisz dostosować flagę w zależności od tego, ile słów ma wejście?
user1686,
@grawity Zaktualizowałem swoją odpowiedź na ogólne rozwiązanie.
αғsнιη
20
filler='===================='
string='foo'

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

Daje

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

${#string}jest długością wartości $stringi ${filler:${#string}}stanowi podciąg $fillerod przesunięcia w ${#string}górę.

Całkowita szerokość wyniku będzie równa maksymalnej szerokości $fillerlub $string.

Łańcuch wypełniający może być w systemach, które jotzostały utworzone dynamicznie przy użyciu

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

(za 16 =w linii). Systemy GNU mogą wykorzystywać seq:

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

Inne systemy mogą używać Perla lub innego szybszego sposobu dynamicznego tworzenia łańcucha.

Kusalananda
źródło
dlaczego nie użyć printfdo wygenerowania filtra, który jest prawie dostępny we wszystkich systemach i rozszerzenia nawiasów klamrowych jak bash/szh?
αғsнιη
@ sddgob Jak byś to zrobił z printfrozszerzeniem nawiasów klamrowych w bash?
Kusalananda
17
printf "%.20s:\n\n" "$str========================="

gdzie %.20sjest format obcinania łańcucha

JJoao
źródło
2
To jest IMHO, lepsze rozwiązanie niż moje. Potrzebuje tylko krótkiego wyjaśnienia ciągu formatu.
Kusalananda
12

Jednym ze sposobów na to:

printf "====================:\r%s\n\n" 'hello world!!'
Satō Katsura
źródło
6
Ha! To sprytna sztuczka! Jednak faktycznie wydrukuje ====================\rhello world, co może być problemem, jeśli OP musi to zapisać, a nie tylko wydrukować na ekranie.
terdon
także echo -e '=================\rHello World!!', ale ma ten sam problem, co @terdon.
αғsнιη
1
@ αғsнιη Tylko jeśli echoobsługuje -e. printfjest prawie zawsze lepszy niż echoz wielu powodów.
Satō Katsura,
5

Podejście Perla:

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

Lub, lepiej, @SatoKatsura wskazał w komentarzach:

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

Jeśli chcesz obsługiwać znaki wielobajtowe UTF, użyj:

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

Ten sam pomysł w powłoce:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"
terdon
źródło
Nie trzeba pętli: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Jednak to (i wszystkie inne dotychczas opublikowane rozwiązania) psuje się, jeśli w grę wchodzą znaki wielobajtowe.
Satō Katsura
@ SatōKatsura ooh, tak, to fajnie! Powinienem o tym pomyśleć, dzięki. I tak, myślałem o dodaniu zrzeczenia się odpowiedzialności za możliwą awarię znaków wielobajtowych UTF, ale pomyślałem, że byłoby to niepotrzebną komplikacją w tym kontekście.
terdon
Myślę, że perl6może to zrobić poprawnie, nawet przy znakach wielobajtowych. Ale z drugiej strony perl6jest denerwujące na wiele sposobów.
Satō Katsura,
@ SatōKatsura cóż, dla tego rodzaju prostej rzeczy powinno wystarczyć ustawienie PERL_UNICODE='AS'. Na przykład: printf '%s' nóóös | perl -nle 'print length($_)'drukuje 8 („źle”), a printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'drukuje 5 („poprawnie”).
terdon
5

Innym sposobem jest użycie tylko printfpolecenia i wygenerowanie wzoru wypełniania znaków najpierw przez Shell Brace Expansion(Możesz zakończyć z obszarem formatowania liczby ≥, w którym chcesz wydrukować {1..end}) i uzyskać tylko każdy jego pierwszy znak, %.1sktóry jest =s, a następnie wydrukować tylko pierwsze 20 znaków długości obszar tego %.20s. Jest to rodzaj lepszego sposobu na powtarzanie znaków / słów zamiast ich powielania.

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

Objaśnienia:

Zwykle jako Brace Expansion , powłoka rozwija się {1..20}w następujący sposób, jeśli je wydrukujemy.

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

Po dodaniu do niej znaku równości ={1..20}powłoka rozwija się w następujący sposób.

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

I printf '%.1s'co to właściwie oznacza printf '%WIDE.LENGTH', drukujemy tylko jedną DŁUGOŚĆ z powyższych z domyślnym 1 SZEROKIM . spowoduje =to tylko s i powtórzy się 20 razy.

Teraz printf '%.20s:\n'drukujemy tylko 20 długości, $stra jeśli długość $str<20, reszta zajmie od wygenerowanych =s do wypełnienia zamiast spacjami.

αғsнιη
źródło