1

I am getting a multi-line output as follows:

Actual Output:

GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU1,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0

In Bash script, I need to convert above output to a single line, with values in one column delimited by # symbol, and two columns separated by a comma.

Expected Output:

GenuineIntel#GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz#Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0#CPU1 and so on.

How to do that?

1

If your input data is in a file called input:

$ awk -F, '/CPU0/{for (i=1; i<=NF;i++) {a[i]=$i};next} {for(i=1;i<=NF-1;i++){printf a[i]"#"$i","}; print a[NF]"#"$NF}' input
GenuineIntel#GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz#Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0#CPU1,64#64,EM64T Family 6 Model 45 Stepping 7#EM64T Family 6 Model 45 Stepping 7,(null)#(null),3093#3093,0#0

Taking the awk command a piece at a time:

  • -F,

    This tells awk to use a comma as the input field separator.

  • /CPU0/{for (i=1; i<=NF;i++) {a[i]=$i};next}

    This begins with /CPU0/ which is an address selector that selects the first line (the one for CPU0). For that line, it stores all the field values in the array a. The command next tells awk to then skip to the next line.

  • for(i=1;i<=NF-1;i++){printf a[i]"#"$i","}

    This tells awk to print column i for the first line, followed by a hashmark followed by column i from the second line, followed by a comma. It does this for all fields save the last.

    Because printf is used, no newline character is printed.

  • print a[NF]"#"$NF}

    This tells awk to print the last field for the first line followed by a hashmark, followed by the last field for the second line.

    Because print is used, the last character printed in a newline, completing the output.

| improve this answer | |
3

Here is a more general awk approach that does not depend on the specific contents of the file:

awk -F, '{for(i=1;i<=NF;i++){a[NR][i]=$(i)}}
         END{
            for(i=1;i<NF;i++){printf "%s#%s,",a[1][i],a[2][i]} 
            print a[1][NF]"#"a[2][NF]
        }' file

Explanation

  • for(i=1;i<=NF;i++){a[NR][i]=$(i)} : this iterates through the fields of each line (which are comma-separated because of the -F,) setting the variable i to all values from 1 to the number of fields (NF). NR is the current line number which, in your example, will be either 1 or 2. a[NR][i]=$(i) sets up a 2 dimensional array, one array per line, and saves each field in it. Basically, the array a will look like:

      1                        2                        3   
    1 1st field of 1st line    2nd field of 1st line    3rd field of 1st line
    2 1st field of 2nd line    2nd field of 2nd line    3rd field of 2nd line
    

    And so on. So, a[1][2] will be the 2nd field of the first line.

  • END{} : this gets executed after the rest of the file has been processed.

  • for(i=1;i<NF;i++){printf "%s#%s,",a[1][i],a[2][i]} : iterate through all fields and print the current field from the first line, an # and the corresponding field of the second line.
  • print a[1][NF]"#"a[2][NF] : print the last field (of each line). This is done separately so we can print the others followed by a comma but this one followed by a newline.

And the same idea in Perl:

perl -F, -ane 'chomp($F[$#F]);
               $k{$.}=\@F; 
               END{
                for($l=0;$l<$#F;$l++){
                  print "${$k{1}}[$l]#${$k{2}}[$l],"
                }
                print "${$k{1}}[$#F]#${$k{2}}[$#F]\n"}' file

This has the advantage of not depending on any specific text being present in the file. It will work for arbitrary lines of data as long as there is the same number of comma separated fields on each line.

| improve this answer | |
2

perl golf:

perl -F, -lane'push@{$f[$_]},$F[$_]for 0..$#F}{$,=",";$"="#";print map"@{$_}",@f'
| improve this answer | |
0
$ cat /tmp/tmp     
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU0,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0     
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU1,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0     
GenuineIntel,Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz,CPU2,64,EM64T Family 6 Model 45 Stepping 7,(null),3093,0    
$ awk -F ',' 'BEGIN{ORS=" ";cpu=",";print "GenuineIntel,"} {gsub(/GenuineIntel/,"");for (i=1;i<=2;i++) {printf $i};print "#";cpu=cpu"#"$3 } END{sub(/,#/,",",cpu);print cpu}' /tmp/tmp
GenuineIntel,  Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz #  Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz #  Intel(R) Xeon(R) CPU E5-2687W 0 @ 3.10GHz # ,CPU0#CPU1#CPU2     

It's not perfect, There is a extra # in last line, it's in front of ,CPU0#CPU1#CPU2, you can delete it.

| improve this answer | |
  • It also assumes that all lines start with GenuineIntel. – terdon Aug 8 '14 at 14:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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