Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a file that looks like the following (the columns are separated by tabs and might contain spaces):

    0637    apple    8528    1392    orange    1390    8528    unknown fruit    7537    1039    banana    1892    0989    pineapple    7537    8528    melon    7537    8528    grape    7537    8528    (null)    (null)

And I need to split it each 3 columns, grepping the ones that will result in [0-9]$ (I'm using awk because is much faster than grep on large files):

0637    apple    8528
1392    orange    1390
8528    unknown fruit    7537
1039    banana    1892
0989    pineapple    7537
8528    melon    7537
8528    grape    7537

Now, I have this beautiful command that does it:

awk -F\\t '{print $2 "\t" $3 "\t" $4 "\n" $5 "\t" $6 "\t" $7 "\n" $8 "\t" $9 "\t" $10 "\n" $11 "\t" $12 "\t" $13 "\n" $14 "\t" $15 "\t" $16 "\n" $17 "\t" $18 "\t" $19 "\n" $20 "\t" $21 "\t" $22 "\n" $23 "\t" $24 "\t" $25}' filename | awk '/[0-9]$/'

I have also an ugly cut command that does the same. I'm still learning awk, so I believe there is a much wiser way to do this. Also, I might get into trouble if there is more than $25. Can you help me?

share|improve this question

5 Answers 5

up vote 2 down vote accepted

This works:

$ cat splitnum.awk
#!/bin/awk -f
BEGIN {
    FS = OFS = "\t"
}
{
    for ( i = 1; i < NF; i = i + 3) {
        if ( $(i+2) ~ /[0-9]+/ ) {
            print $i, $(i+1), $(i+2)
        }
    }
}
$ awk -f splitnum.awk filename

Or all on one line:

awk 'BEGIN{FS=OFS="\t"}{for (i=1;i<NF;i=i+3){if ($(i+2) ~ /[0-9]+/) {print $i, $(i+1), $(i+2)}}}' filename

Basically cycling over the data fields, three at a time, checking that the third is made up of numerals.

share|improve this answer
    
It doesn't work with the example above (replacing four spaces with tabs). –  Teresa e Junior Feb 4 '12 at 7:31
    
Oh, I see! It works if I remove the first tab (every line starts with a tab before data). –  Teresa e Junior Feb 4 '12 at 7:36
    
for (i=2; i<NF; i=i+3) to ignore first tab –  Teresa e Junior Feb 4 '12 at 15:42
1  
@Arcege nit: $ awk -f whatever.awk is not necessary if the shebang #! has already specified /bin/awk -f. In your case, $ ./splitnum.awk filename will work a treat. The gain is: less typing effort. –  syntaxerror Dec 15 at 19:21
awk -F'\t' '{for(i=1;i<=NF;i++)if(!(i%3))$i=$i "\n"}1' file
share|improve this answer
    
Almost! The resulting columns should be separated by tabs too to avoid confusion. And blank spaces and some empty lines are being printed. –  Teresa e Junior Feb 4 '12 at 7:42

For awk haters:

perl -pe 's/(\d+)\s+(\d+)/$1\n$2/g' | 
perl -lnpe 's/^\s*(\d+)\s+([^\d]+?)\s+(\d+).*/$1\t$2\t$3/' |
egrep '[0-9]$'
share|improve this answer

An even worse perl answer:

perl -pnle 's/\s+/\t/g;s/(\d)\t(\d)/$1\n$2/g;s/^\s+|\s+$//;s/.*[^\d]$//'|grep .
share|improve this answer
    
Thank you, but both perl commands don't give the desired output. –  Teresa e Junior Feb 4 '12 at 14:33

Supposing the fields are separated by one or more tabs, and that each field can contain spaces but not tabs, then the following should work

( IFS=$'\t'; printf '%s %s %s\n' $(<input_file) )

(the subshell is convenient to not change IFS for the calling shell).

Alternatively, if fields are separated by one or more spaces and tabs, and that each field do not contain spaces nor tabs, then the following should work

printf '%s %s %s\n' $(<input_file)
share|improve this answer
    
Man, this is awesome :D Could you please explain what $ means in IFS? –  Teresa e Junior Feb 4 '12 at 14:35
    
IFS=$'\t' means to use a literal tab character as an Input Fields Separator. –  enzotib Feb 4 '12 at 14:49
    
Unfortunately it fails if fruit is empty! –  Teresa e Junior Feb 4 '12 at 15:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.