Solo in bash, nessun comando esterno:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
o come versione a tubo a una riga:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
Il modo in cui funziona è convertire ogni carattere della stringa in "(.)" Per far corrispondere e catturare regex =~
, quindi emettere le espressioni catturate BASH_REMATCH[]
dall'array, raggruppate come richiesto. Gli spazi iniziali / finali / intermedi vengono conservati, rimuovere le virgolette intorno "${BASH_REMATCH[@]:1}"
per ometterle.
Qui è racchiuso in una funzione, questo elaborerà i suoi argomenti o leggerà stdin se non ci sono argomenti:
function fmt4() {
while IFS= read -r str; do
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
done < <( (( $# )) && printf '%s\n' "$@" || printf '%s\n' $(< /dev/stdin) )
}
$ echo foobarbazblargblurg | fmt4
foob arba zbla rgbl urg
È possibile parametrizzare facilmente il conteggio per adattare di conseguenza la stringa di formato.
Viene aggiunto uno spazio finale, utilizzare due printf
s anziché uno se questo è un problema:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
Il primo printf
stampa (fino a) i primi 4 caratteri, il secondo stampa condizionatamente tutto il resto (se presente) con uno spazio iniziale per separare i gruppi. Il test è per 5 elementi non 4 per tenere conto dell'elemento zeroth.
Gli appunti:
- shell
printf
s' %c
potrebbero essere usati al posto di %s
, %c
(forse) libera l'intento chiaro, ma non è multi-byte sicuro carattere. Se la tua versione di bash è in grado, quanto sopra è sicuro per tutti i caratteri multi-byte.
- shell
printf
riutilizza la sua stringa di formato fino a quando non esaurisce gli argomenti, quindi divora solo 4 argomenti alla volta e gestisce gli argomenti finali (quindi non sono necessari casi limite, a differenza di alcune delle altre risposte qui che sono probabilmente sbagliate)
BASH_REMATCH[0]
è l'intera stringa corrispondente, quindi solo output a partire dall'indice 1
- utilizzare
printf -v myvar ...
invece per archiviare in una variabile myvar
(soggetto al solito comportamento di ciclo di lettura / subshell)
- aggiungere
printf "\n"
se necessario
È possibile far funzionare quanto sopra zsh
se si utilizza l'array match[]
anziché BASH_REMATCH[]
e sottrarre 1 da tutti gli indici poiché zsh
non mantiene un elemento 0 con l'intera corrispondenza.
sed
primo tentativo che ho potuto calciare.