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 printf
s 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 printf
imprime (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
printf
s' %c
podrí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
printf
reutiliza 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 zsh
si usa la matriz en match[]
lugar de BASH_REMATCH[]
, y restar 1 de todos los índices, ya zsh
que no mantiene un elemento 0 con toda la coincidencia.
sed
que intenté primero que podía patearme.