Question

I have some data on a single line like below

abc edf xyz rfg yeg udh

I want to present the data as below

abc
xyz
yeg


edf
rfg
udh

so that alternate fields are printed with newline separated. Are there any one liners for this?

Was it helpful?

Solution

The following awk script can do it:

> 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

OTHER TIPS

Python in the same spirit as the above awk (4 lines):

$ 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'

Python 1-liner (omitting the pipe to it which is identical):

$ python -c 'f=raw_input().split(); print "\n".join(f[::2] + [""] + f[1::2])'

Another Perl 5 version:

#!/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;

A shame that the previous perl answers are so long. Here are two perl one-liners:

echo 'abc edf xyz rfg yeg udh'|
  perl -naE '++$i%2 and say for @F; ++$j%2 and say for "",@F'

On older versions of perl (without "say"), you may use this:

echo 'abc edf xyz rfg yeg udh'|
  perl -nae 'push @{$a[++$i%2]},"$_\n" for "",@F; print map{@$_}@a;'

Just for comparison, here's a few Perl scripts to do it (TMTOWTDI, after all). A rather functional style:

#!/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";

We could also do it closer to the AWK script:

#!/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;

I've run out of ways to do it, so if any other clever Perlers come up with another method, feel free to add it.

Another Perl solution:

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], '');
}

You could even condense it into a real one-liner:

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], "");'

Here's the too-literal, non-scalable, ultra-short awk version:

awk '{printf "%s\n%s\n%s\n\n%s\n%s\n%s\n",$1,$3,$5,$2,$4,$6}'

Slightly longer (two more characters), using nested loops (prints an extra newline at the end):

awk '{for(i=1;i<=2;i++){for(j=i;j<=NF;j+=2)print $j;print ""}}'

Doesn't print an extra newline:

awk '{for(i=1;i<=2;i++){for(j=i;j<=NF;j+=2)print $j;if(i==1)print ""}}'

For comparison, paxdiablo's version with all unnecessary characters removed (1, 9 or 11 more characters):

awk '{for(i=1;i<=NF;i+=2)print $i;print "";for(i=2;i<=NF;i+=2)print $i}'

Here's an all-Bash version:

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

My attempt in 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>

you could also just use tr: echo "abc edf xyz rfg yeg udh" | tr ' ' '\n'

Ruby versions for comparison:

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

For newlines, i leave it to you to do yourself.

Here is yet another way, using Bash, to manually rearrange words in a line - with previous conversion to an 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 

Cheers!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top