Solo en bash, no hay comandos externos:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
o como una versión de tubería de una línea:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
La forma en que esto funciona es convertir cada carácter de la cadena en un "(.)" Para la coincidencia de =~expresiones regulares y capturar , y luego generar las expresiones capturadas de la BASH_REMATCH[]matriz, agrupadas según sea necesario. Los espacios iniciales / finales / intermedios se conservan, elimine las comillas "${BASH_REMATCH[@]:1}"para omitirlos.
Aquí está envuelto en una función, esta procesará sus argumentos o leerá stdin si no hay argumentos:
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
Puede parametrizar fácilmente el recuento para ajustar la cadena de formato en consecuencia.
Se agrega un espacio final, use dos printfs en lugar de uno si eso es un problema:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
El primero printfimprime (hasta) los primeros 4 caracteres, el segundo imprime condicionalmente todo el resto (si corresponde) con un espacio inicial para separar los grupos. La prueba es para 5 elementos, no 4 para dar cuenta del elemento cero.
Notas:
- shell
printfs' %cpodría ser utilizado en lugar de %s, %c(tal vez) hace más clara la intención, pero no es multi-byte carácter seguro. Si su versión de bash es capaz, lo anterior es seguro para todos los caracteres de varios bytes.
- Shell
printfreutiliza su cadena de formato hasta que se quede sin argumentos, por lo que solo engulle 4 argumentos a la vez y maneja los argumentos finales (por lo que no se necesitan casos extremos, a diferencia de algunas de las otras respuestas aquí que posiblemente sean incorrectas)
BASH_REMATCH[0] es toda la cadena coincidente, por lo que solo la salida comienza desde el índice 1
- use
printf -v myvar ...en su lugar para almacenar en una variable myvar(sujeto al comportamiento habitual de bucle de lectura / subshell)
- agregar
printf "\n"si es necesario
Puede hacer que lo anterior funcione zshsi usa la matriz en match[]lugar de BASH_REMATCH[], y restar 1 de todos los índices, ya zshque no mantiene un elemento 0 con toda la coincidencia.
sedque intenté primero que podía patearme.