awk :
awk '{ c=0; for(i=length($2);i>0;i-=4) {a[++c]=(i-4>0)? substr($2,i-4+1,4) : substr($2,1,i)}
$2=""; for(i=length(a);i>0;i--) $2=$2 FS a[i] }1' file
A saída:
1. pla cing
2. back tick
3. pa ragr aphs
Como posso dividir as letras de uma palavra e entre um único espaço, com cada uma das quatro últimas letras em uma linha? Por exemplo, Dado,
1. placing
2. backtick
3. paragraphs
Eu gostaria de ver abaixo
1. pla cing
2. back tick
3. pa ragr aphs
awk :
awk '{ c=0; for(i=length($2);i>0;i-=4) {a[++c]=(i-4>0)? substr($2,i-4+1,4) : substr($2,1,i)}
$2=""; for(i=length(a);i>0;i--) $2=$2 FS a[i] }1' file
A saída:
1. pla cing
2. back tick
3. pa ragr aphs
Com Perl
usando lookarounds
, poderíamos executar a operação como:
perl -pe 's/(?<=\w)(?=(?:\w{4})+$)/ /g'
Que se traduz como: quando em pé em uma posição, à nossa esquerda é um alfanumérico e à nossa direita são pelo menos 4 alnums ou múltiplos dos mesmos até o final da string. Quando tal posição existe, um espaço é colocado lá. Fazer isso afeta globalmente a mudança solicitada.
Poderíamos usar bash
para fazer isso também:
#!/bin/bash
# symbolic constants
NL=$'2'; # newline
SP=$'0'; # space
# elementary regexes
alnum='[0-9a-zA-Z]'; # a single alphanumeric
alnums4=$(csh -c 'repeat 4 echo -n "$1"' "$alnum"); # 4 consecutive alnums
# main processing
while IFS= read -r line res; do
while c4=$(expr "$SP$line$NL" : ".*$alnum\($alnums4\)$NL")
do
res=${c4}${res:+"$SP"}${res-} line=${line%????}
done
printf '%s %s\n' "$line" "$res"
done
Usando o editor GNU sed
:
sed -Ee '
s/\S+/\n&\n/2; # enclose the 2nd field with markers
# a do-while loop to progessively move the right marker to the left,
# consuming 4 alnums in each iteration. Looping stops when 4 alnums+
# 1 alnum at the boundary remains.
:loop
s/(\n[[:alnum:]].*)([[:alnum:]]{4})\n/\n /
tloop
# clear out the markers when done
s/\n//g
'
Com sed
, você pode fazer algo como:
sed '
G
:1
s/\([[:alpha:]]\)\([[:alpha:]]\{4\}\)\(\n\)/ /
t1
s/\n//
'
Usamos um caractere de nova linha como um marcador em execução (nova linha é o caractere que não aparecerá dentro do espaço de padrão inicial). Nós adicionamos no final inicialmente. Então, enquanto encontrarmos ABCDE<marker>
(onde ABCDE são 5 caracteres alfabéticos, você pode substituir [[:space:]]
por [^[:blank:]]
se você quiser considerar palavras como sequências de não-brancos ao invés de seqüências de letras), nós o substituímos com A<marker> BCDE
e loop. E nós removemos o marcador no final.
Dessa forma, temos certeza de processar apenas a palavra que está no final da linha apenas.
Se você quisesse quebrar cada palavra, não apenas a última palavra, seria mais simples:
sed -e :1 -e 's/\(.*[[:alpha:]]\)\([[:alpha:]]\{4\}\)/ /;t1'
Se a sua entrada contiver caracteres decompostos (como na saída de printf 'abcd\u00e9e\u0301f\n'
: abcdééf
), você poderá fazer:
perl -Mopen=locale -lpe 'while(s/.*(?=\w)\X\K(?:(?=\w)\X){4}/ $&/){}'
Usando o Perl (e assumindo apenas caracteres de byte único):
perl -ne 'print scalar(reverse join " ", (reverse =~ /.{1,4}/g)), "\n"'
reverse
interno inverte a palavra dada (na verdade, toda a linha de entrada, independentemente do conteúdo). join
unirá esses fragmentos em uma string, mas com espaços intermediários. reverse
inverte a string unida. scalar
é usado para forçar o reverse
externo a operar em contexto escalar. print
produzirá o resultado. O código abaixo faz a mesma coisa, mas acaba com scalar
, "\n"
e print
usando -p
, -l
e atribuindo a $_
:
perl -lpe '$_ = reverse join " ", reverse =~ /.{1,4}/g'