Riempiendo gli spazi bianchi in una stringa con un altro carattere


12

Vorrei produrre hello worldoltre 20 caratteri.

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

# Actual output
hello world!!        :

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

Tuttavia, non voglio completare con spazi ma con " = " invece. Come lo faccio?

Risposte:


9

Risposta aggiornata per essere una soluzione più generale. vedi anche la mia altra risposta di seguito usando solo l'espansione delle parentesi graffe e pritnf.

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

Come funziona?

questo (=*):$/cattura uno spazio, uno o più =che è seguito da due punti :alla fine del suo input; facciamo l'insieme di =come una partita di gruppo e \1sarà il suo riferimento indietro.

Con :loopabbiamo definito un'etichetta denominata loope con t loopessa salterà a quell'etichetta quando una s/ (=*):$/\1=:/sostituzione ha avuto esito positivo;

Nella parte di ricambio con \1=:, incrementa sempre il numero di se =riporta i due punti alla fine della stringa.


1
quindi devi regolare il flag in base a quante parole ha l'input?
user1686

@grawity Ho aggiornato la mia risposta alla soluzione generale ora.
αғsнιη,

20
filler='===================='
string='foo'

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

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

${#string}è la lunghezza del valore $stringed ${filler:${#string}}è la sottostringa $fillerdell'offset in ${#string}avanti.

La larghezza totale dell'output sarà quella della larghezza massima di $fillero $string.

La stringa di riempimento può, sui sistemi che ha jot, essere creata in modo dinamico utilizzando

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

(per 16 =in una riga). I sistemi GNU possono usare seq:

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

Altri sistemi possono usare il Perl o qualche altro modo più veloce per creare dinamicamente la stringa.


perché non usare printfper generare il filtro che è quasi disponibile in tutti i sistemi e l'espansione del controvento con le shell come bash/szh?
αғsнιη,

@sddgob Come lo faresti con l' printfespansione + parentesi graffa bash?
Kusalananda


17
printf "%.20s:\n\n" "$str========================="

dove %.20sè il formato di troncamento della stringa


2
Questa è IMHO, una soluzione migliore della mia. Ha solo bisogno di una breve spiegazione della stringa di formato.
Kusalananda

12

Un modo per farlo:

printf "====================:\r%s\n\n" 'hello world!!'

6
Ha! È un trucco intelligente! Tuttavia, verrà effettivamente stampato ====================\rhello world, il che potrebbe essere un problema se l'OP deve memorizzarlo e non stamparlo solo sullo schermo.
terdon

anche echo -e '=================\rHello World!!', ma ha lo stesso problema che ha sottolineato @terdon.
αғsнιη,

1
@ αғsнιη Solo se echosupporta -e. printfè quasi sempre meglio di echo, per molte ragioni.
Satō Katsura,

5

Un approccio Perl:

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

O meglio, @SatoKatsura ha sottolineato nei commenti:

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

Se è necessario supportare caratteri multi-byte UTF, utilizzare:

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

Stessa idea nella shell:

v='hello world!!'; while [ ${#v} -lt 20 ]; do v="$v""="; done; printf '%s\n\n' "$v"

Non hai bisogno di un ciclo: perl -le '$k = "hello world!!"; print $k, "=" x (20-length $k), "\n"'. Tuttavia, questa (e tutte le altre soluzioni pubblicate finora) si interrompe se sono coinvolti caratteri multibyte.
Satō Katsura,

@ SatōKatsura ooh, sì, è pulito! Avrei dovuto pensarci, grazie. E sì, stavo pensando di aggiungere una dichiarazione di non responsabilità per un possibile errore sui caratteri multi-byte UTF, ma ho pensato che sarebbe stata una complicazione inutile in questo contesto.
terdon

Penso che perl6potrebbe avere un modo per farlo correttamente anche con caratteri multi-byte. Ma d'altra parte perl6è fastidioso in molti modi.
Satō Katsura,

@ SatōKatsura bene, per questo tipo di cose semplici, dovrebbe essere sufficiente impostare PERL_UNICODE='AS'. Ad esempio: printf '%s' nóóös | perl -nle 'print length($_)'stampa 8 ("errato") mentre printf '%s' nóóös | PERL_UNICODE='AS' perl -nle 'print length($_)'stampa 5 ("corretto").
terdon

5

Un altro modo è usare solo il printfcomando e generare prima il motivo di riempimento dei caratteri per Shell Brace Expansion(È possibile terminare con un numero ≥ area di formattazione in cui si desidera stampare {1..end}) e ottenere solo ogni primo carattere di esso %.1sche è se =quindi stampare solo i primi 20 caratteri di lunghezza area di quello %.20s. Questo è un modo migliore per avere caratteri / parole ripetuti invece di duplicarli.

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

spiegazioni:

Normalmente come Brace Expansion , la shell si espande {1..20}come segue se le stampiamo.

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

Quindi con l'aggiunta di un segno uguale ad esso ={1..20}, la shell si espanderà come segue.

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

E con ciò printf '%.1s'che in realtà significa printf '%WIDE.LENGTH', stiamo stampando solo una LUNGHEZZA di quelle sopra con 1 WIDE predefinito . così risulterà =solo e 20 volte ripetuto.

Ora con printf '%.20s:\n'stiamo stampando solo la lunghezza di 20 $stre se la lunghezza è $str<20, il resto richiederà da =s generati da riempire invece di spazi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.