Como insiro um espaço a cada quatro caracteres em uma linha longa?

30

Eu tenho uma linha longa que quero inserir um espaço a cada 4 caracteres, em uma única linha solitária de texto sólido para facilitar a leitura, qual é a maneira mais simples de fazer isso? também eu deveria poder inserir a linha de um cano. por exemplo

echo "foobarbazblargblurg" | <some command here>

foob arba zbla rgbl urg
xenoterracida
fonte

Respostas:

54

Use sed da seguinte maneira:

$ echo "foobarbazblargblurg" | sed 's/.\{4\}/& /g'
foob arba zbla rgbl urg
dogbane
fonte
11
xingando tão perto do que sedeu tentei primeiro eu poderia me chutar.
Xenoterracide
7
Apenas curioso, o que o '&' realiza? Ah, é um argumento para 'a coisa que acabou de combinar'. Eu tolo.
Onipático
11
deve-se notar que este adiciona um espaço no fim, bem como se há mais um caractere na seqüência, que pode não ser desejável
Anubis
@Anubis's/.\{4\}/& /g;s/ $//'
wieczorek1990
20

Você pode usar o seguinte exemplo simples:

$ echo "foobarbazblargblurg" | fold -w4 | paste -sd' ' -
foob arba zbla rgbl
kenorb
fonte
Muito bom ... acho que isso é ainda melhor que a sedresposta. Eu não sabia foldantes.
Curinga
11
Infelizmente, nas versões atuais do GNU fold, ele não funciona com caracteres de vários bytes (como echo €€€€€€€€ | fold -w4 | paste -sd' ' -em UTF-8).
Stéphane Chazelas 5/05
3

Aqui está o exemplo usando grepe xargs:

$ echo "foobarbazblargblurg" | grep -o .... | xargs
foob arba zbla rgbl
kenorb
fonte
xargsé executado echopor padrão, portanto, não funcionará com palavras como -nenou que contenham barras invertidas, dependendo da echoimplementação. Você verá o caractere de nova linha ímpar de vez em quando, se o xargs executar mais de um echo. Melhor canalizar para o paste -sd ' ' -lugar. Observe que -onão é uma opção padrão.
Stéphane Chazelas 5/05
3

Somente no bash, sem comandos externos:

str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"

ou como uma versão de tubo de uma linha:

echo foobarbazblargblurg | 
  { IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
    printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }

A maneira como isso funciona é converter cada caractere da sequência de caracteres em um "(.)" Para corresponder e capturar regex com =~, e apenas gerar as expressões capturadas da BASH_REMATCH[]matriz, agrupadas conforme necessário. Os espaços à esquerda / à direita / intermediários são preservados, remova as aspas "${BASH_REMATCH[@]:1}"para omiti-las.

Aqui está envolvido em uma função, esta processará seus argumentos ou lerá stdin se não houver 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 

Você pode facilmente parametrizar a contagem para ajustar a sequência de formatação de acordo.

Um espaço à direita é adicionado, use dois printfs em vez de um se isso for um problema:

printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"

A primeira printfimprime (até) os 4 primeiros caracteres, a segunda imprime condicionalmente todo o restante (se houver) com um espaço à esquerda para separar os grupos. O teste é para 5 elementos e não 4 para dar conta do elemento zeroth.

Notas:

  • shell printfs' %cpode ser usado em vez de %s, %c(talvez) faz a intenção mais clara, mas não é multi-byte caráter. Se a sua versão do bash for capaz, as opções acima são seguras para caracteres de vários bytes.
  • O shell printfreutiliza sua string de formatação até ficar sem argumentos, portanto, apenas devolve 4 argumentos por vez e lida com os argumentos à direita (portanto, não são necessários casos extremos, ao contrário de algumas das outras respostas aqui que estão indiscutivelmente erradas)
  • BASH_REMATCH[0] é a sequência correspondente completa, portanto, apenas a saída começa no índice 1
  • em printf -v myvar ...vez disso, use para armazenar em uma variável myvar(sujeita ao comportamento usual de leitura / loop / subcamação)
  • adicione printf "\n"se necessário

Você pode fazer o trabalho acima zshse usar a matriz em match[]vez de BASH_REMATCH[]e subtrair 1 de todos os índices, pois zshnão mantém um elemento 0 com a correspondência inteira.

mr.spuratic
fonte
3

Com zshapenas:

str=foobarbazblargblurg

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

Ou

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

com ksh93apenas:

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

Somente com qualquer shell POSIX (evitando também o espaço à direita se o comprimento da entrada for 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"

Agora, isso é para personagens . Se você quiser fazer isso em clusters de grafema (por exemplo, quebrar Stéphane, escritos como $'Ste\u0301phane', como Stép hanee não Ste phan e), com zsh:

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

Com o ksh93, você também pode alterar a largura de exibição, o que funcionaria para isso Stéphaneacima, mas também pode ajudar quando outros tipos de caracteres de largura zero ou largura dupla estão envolvidos:

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
fonte
2

Vou responder inserindo apenas os espaços necessários, para que um espaço apareça pelo menos a cada 4 caracteres em uma linha; não tenho certeza de como você deseja lidar com este caso. Por exemplo, dada a entrada de "aa bbccdd", você obteria a saída "aa bbcc dd" em vez de "aa b bccd d".

Estou usando o Perl para procurar, mas não estou muito familiarizado com o Perl em geral, portanto, pode haver ajustes necessários:

$ 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
fonte
0

Eu fiz isso usando python

Primeiro eu estou lendo o arquivo, então eu estou dividindo por 4 caracteres e adicionando espaço

#!/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 no conteúdo que você forneceu no exemplo

saída

foob arba zbla rgbl
Praveen Kumar BS
fonte