¿Cómo inserto un espacio cada cuatro caracteres en una línea larga?

30

Tengo una línea larga en la que quiero insertar un espacio cada 4 caracteres, en una sola línea de texto sólido para que sea más fácil de leer, ¿cuál es la forma más sencilla de hacerlo? También debería poder ingresar la línea desde una tubería. p.ej

echo "foobarbazblargblurg" | <some command here>

da

foob arba zbla rgbl urg
xenoterracida
fuente

Respuestas:

54

Use sed de la siguiente manera:

$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg
dogbane
fuente
1
maldiciendo que estaba tan cerca de lo sedque intenté primero que podía patearme.
xenoterracide
77
Por curiosidad, ¿cuál es el '&' lograr? Oh, es un sustituto de 'la cosa que acaba de coincidir'. Tonto de mí.
Omnifarioso
1
Cabe señalar que esto agrega un espacio al final también si hay un carácter más en la cadena, lo que podría no ser deseable
Anubis
@Anubis's/.\{4\}/& /g;s/ $//'
wieczorek1990
21

Puede usar el siguiente ejemplo simple:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
kenorb
fuente
Muy bien ... Creo que esto es incluso mejor que la sedrespuesta. No lo sabía foldantes.
Comodín el
1
Desafortunadamente, con las versiones actuales de GNU fold, no funciona con caracteres de varios bytes (como echo €€€€€€€€ | fold -w4 | paste -sd' ' -en UTF-8).
Stéphane Chazelas
3

Aquí está el ejemplo usando grepy xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl
kenorb
fuente
xargsse ejecuta echode forma predeterminada, por lo que no funcionará con palabras como -neno que contengan barras diagonales inversas dependiendo de la echoimplementación. Verá el extraño carácter de nueva línea de vez en cuando también si xargs ejecuta más de uno echo. Mejor canalizar a su paste -sd ' ' -lugar. Tenga en cuenta que -ono es una opción estándar.
Stéphane Chazelas
3

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.

Sr. púrpura
fuente
3

Con zshsolo:

str=foobarbazblargblurg

set -o extendedglob
printf '%s\n' ${str//(#m)????/$MATCH }

O

printf '%s%s%s%s ' ${(s::)str}

con ksh93solo:

printf '%s\n' "${str//????/\0 }"

Con cualquier shell POSIX solamente (también evitando el espacio final si la longitud de entrada es un múltiplo de 4):

out=
while true; do
  case $str in
    (?????*)
      new_str=${str#????}
      out=$out${str%"$new_str"}' '
      str=$new_str
      ;;
    (*)
      out=$out$str
      break
  esac
done
printf '%s\n' "$out"

Ahora, eso es para los personajes . Si desea hacerlo en grupos de grafemas (por ejemplo, para romper Stéphane, escrito como $'Ste\u0301phane', como Stép haney no Ste phan e), con zsh:

set -o rematchpcre
str=$'Ste\u301phane' out=
while [[ $str =~ '(\X{4})(.+)' ]] {
  out+="$match[1] " str=$match[2]
}
out+=$str
printf '%s\n' $out

Con ksh93, también podría dividir el ancho de la pantalla, lo que funcionaría para lo Stéphaneanterior, pero también podría ayudar cuando intervienen otros tipos de caracteres de ancho cero o doble ancho:

str=$'Ste\u301phane' out=
while
  start=${ printf %L.4s. "$str"; }
  start=${start%.}
  [ "$start" != "$str" ]
do
  out+="$start " str=${str#"$start"}
done
out+=$str
printf '%s\n' "$out"
Stéphane Chazelas
fuente
2

Voy a responder solo insertando espacios según sea necesario para que aparezca un espacio al menos después de cada 4 caracteres en una línea; No estoy seguro de qué manera quiere manejar este caso. Por ejemplo, dada la entrada de "aa bbccdd", obtendría la salida "aa bbcc dd" en lugar de "aa b bccd d".

Estoy usando Perl para mirar hacia adelante, pero no estoy muy familiarizado con Perl en general, por lo que es posible que se necesiten ajustes:

$ echo "foobarbazblargblurg" | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
foob arba zbla rgbl urg

$ echo 'aa bbccdd' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
aa bbcc dd
# not 'aa b bccd d'!

$ echo 'some input' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g'
some inpu t
# not 'some  inp ut'!

$ echo $'aabb\nc cddee' | perl -wp -e 's/[^ ]{4}(?=[^\n ])/$& /g' | 
> while read; do echo "${REPLY}x"; done
aabbx
c cdde ex
# no spaces added at the end of the first line (while loop to add to the end of
# the line and show this)
Fred Nurk
fuente
0

He hecho esto usando Python

Primero estoy leyendo el archivo, luego estoy dividiendo entre 4 caracteres y agregando espacio

#!/usr/bin/python
import re
b=re.compile(r'[a-z]{4}')

p=open('/root/l.txt','r')
i=p.readlines()
for j in i:
    m=re.findall(b,j)
print " " .join (m) + "  "

/root/l.txt ==> Consiste en el contenido que proporcionó en el ejemplo

salida

foob arba zbla rgbl
Praveen Kumar BS
fuente