Como faço para dividir uma linha e reorganizar seus elementos?
Pergunta
Eu tenho alguns dados em uma única linha como abaixo
abc edf xyz rfg yeg udh
Eu quero apresentar os dados como abaixo
abc
xyz
yeg
edf
rfg
udh
de modo que os campos alternados são impressos com nova linha separada. Há algum forros para isso?
Solução
O script a seguir awk
pode fazê-lo:
> echo 'abc edf xyz rfg yeg udh' | awk '{
for (i = 1;i<=NF;i+=2){print $i}
print "";
for (i = 2;i<=NF;i+=2){print $i}
}'
abc
xyz
yeg
edf
rfg
udh
Outras dicas
Python com o mesmo espírito como o awk acima (4 linhas):
$ echo 'abc edf xyz rfg yeg udh' | python -c 'f=raw_input().split()
> for x in f[::2]: print x
> print
> for x in f[1::2]: print x'
pitão 1-forro (omitindo-se o tubo a ele o qual é idêntico):
$ python -c 'f=raw_input().split(); print "\n".join(f[::2] + [""] + f[1::2])'
Outra versão Perl 5:
#!/usr/bin/env perl
use Modern::Perl;
use List::MoreUtils qw(part);
my $line = 'abc edf xyz rfg yeg udh';
my @fields = split /\s+/, $line; # split on whitespace
# Divide into odd and even-indexed elements
my $i = 0;
my ($first, $second) = part { $i++ % 2 } @fields;
# print them out
say for @$first;
say ''; # Newline
say for @$second;
Uma vergonha que as respostas perl anteriores são tão longas. Aqui estão dois Perl one-liners:
echo 'abc edf xyz rfg yeg udh'|
perl -naE '++$i%2 and say for @F; ++$j%2 and say for "",@F'
Em versões mais antigas do perl (sem "dizer"), você pode usar isto:
echo 'abc edf xyz rfg yeg udh'|
perl -nae 'push @{$a[++$i%2]},"$_\n" for "",@F; print map{@$_}@a;'
Apenas para comparação, a seguir, alguns scripts Perl para fazê-lo (TMTOWTDI, depois de tudo). Um estilo bastante funcional:
#!/usr/bin/perl -p
use strict;
use warnings;
my @a = split;
my @i = map { $_ * 2 } 0 .. $#a / 2;
print join("\n", @a[@i]), "\n\n",
join("\n", @a[map { $_ + 1 } @i]), "\n";
Nós também poderia fazê-lo mais perto para o script AWK:
#!/usr/bin/perl -p
use strict;
use warnings;
my @a = split;
my @i = map { $_ * 2 } 0 .. $#a / 2;
print "$a[$_]\n" for @i;
print "\n";
print "$a[$_+1]\n" for @i;
Já corri para fora de maneiras de fazer isso, então se quaisquer outros Perlers inteligentes vir para cima com outro método, fique à vontade para adicioná-lo.
Outra solução Perl:
use strict;
use warnings;
while (<>)
{
my @a = split;
my @b = map { $a[2 * ($_%(@a/2)) + int($_ / (@a /2))] . "\n" } (0 .. @a-1);
print join("\n", @a[0..((@b/2)-1)], '', @a[(@b/2)..@b-1], '');
}
Você até poderia condensá-lo em um one-liner real:
perl -nwle'my @a = split;my @b = map { $a[2 * ($_%(@a/2)) + int($_ / (@a /2))] . "\n" } (0 .. @a-1);print join("\n", @a[0..((@b/2)-1)], "", @a[(@b/2)..@b-1], "");'
Aqui está o demasiado literal, não escalável, versão awk
ultra-curto:
awk '{printf "%s\n%s\n%s\n\n%s\n%s\n%s\n",$1,$3,$5,$2,$4,$6}'
ligeiramente mais longa (mais dois caracteres), utilizando laços aninhados (imprime uma nova linha extra no final):
awk '{for(i=1;i<=2;i++){for(j=i;j<=NF;j+=2)print $j;print ""}}'
Não imprime uma nova linha extra:
awk '{for(i=1;i<=2;i++){for(j=i;j<=NF;j+=2)print $j;if(i==1)print ""}}'
Para comparação,
awk '{for(i=1;i<=NF;i+=2)print $i;print "";for(i=2;i<=NF;i+=2)print $i}'
Aqui está uma versão todo-Bash:
d=(abc edf xyz rfg yeg udh)
i="0 2 4 1 3 5"
for w in $i
do
echo ${d[$w]}
[[ $w == 4 ]]&&echo
done
Minha tentativa em Haskell:
Prelude> (\(x,y) -> putStr $ unlines $ map snd (x ++ [(True, "")] ++ y)) $ List.partition fst $ zip (cycle [True, False]) (words "abc edf xyz rfg yeg udh")
abc
xyz
yeg
edf
rfg
udh
Prelude>
Você também pode usar apenas tr:
echo "abc edf xyz rfg yeg udh" | tr ' ' '\n'
versões Ruby para comparação:
ARGF.each do |line|
groups = line.split
0.step(groups.length-1, 2) { |x| puts groups[x] }
puts
1.step(groups.length-1, 2) { |x| puts groups[x] }
end
ARGF.each do |line|
groups = line.split
puts groups.select { |x| groups.index(x) % 2 == 0 }
puts
puts groups.select { |x| groups.index(x) % 2 != 0 }
end
$ echo 'abc edf xyz rfg yeg udh' |awk -vRS=" " 'NR%2;NR%2==0{_[++d]=$0}END{for(i=1;i<=d;i++)print _[i]}'
abc
xyz
yeg
edf
rfg
udh
Para novas linhas, eu deixá-lo para que você faça a si mesmo.
Aqui está ainda outra forma, usando Bash, às palavras manualmente Reorganizar em uma linha - com a conversão anterior para um array:
echo 'abc edf xyz rfg yeg udh' | while read tline; do twrds=($(echo $tline)); echo -e "${twrd[0]} \n${twrd[2]} \n${twrd[4]} \n\n ${twrd[1]} \n${twrd[3]} \n${twrd[5]} \n" ; done
Felicidades!