2

I have lines in a file that are comma separated. There are no column headers, it's mostly comma separated 'name=value' pairs. Here is some test data:

listoffruits,producelist,APPLE=red,BANNANA=yellow,GRAPE=purple,ORANGE=orange,FRUIT=yes,WATERMELON=green
listoffruits,producelist,APPLE=red,BANNANA=yellow,GRAPE=violet,ORANGE=orange,FRUIT=affirmative,WATERMELON=green

I would like to get the value for GRAPE=* and FRUIT=* to get the result:

purple yes
violet affirmative

Also I would like the ability to add more "columns" later (so not always GRAPE, FRUIT, but GRAPE, FRUIT and WATERMELON)

Another snag is the columns are not fixed. So I don't always know that WATERMELON is the last column.

The closest I got was from @jasonwryan:

awk -v RS="," -F= '/GRAPE/{a=$2}; /FRUIT/{b=$2} END{print a,b"\n"}'

but this outputs the last line "violet affirmative" and not:

purple yes

violet affirmative
  • Are those files or streams? In any event, it significantly changes the required approach; you are probably best asking another question. – jasonwryan May 10 '15 at 1:54
  • 1
    The solutions I give in my answer should all work with your new question. Next time, please make sure you explain your needs clearly so we don't waste time providing answers that don't help you. – terdon May 11 '15 at 10:22
4

Some more choices. I have saved your example text in file for simplicity.

  1. grep and PCREs:

    $ grep -oP '(GRAPE|FRUIT)=\K.*?(?=,)' file 
    purple
    yes
    violet
    affirmative
    

    To get them on the same line, just parse. For example

    $ grep -oP '(GRAPE|FRUIT)=\K.*?(?=,)' | paste -d" " - - –  
    purple yes
    violet affirmative
    
  2. sed

    $ sed 's/.*GRAPE=\([^,]*\).*FRUIT=\([^,]*\).*/\1 \2/' file 
    purple yes
    violet affirmative
    

    Or, with GNU sed

    $ sed -r 's/.*GRAPE=([^,]*).*FRUIT=([^,]*).*/\1 \2/' file 
    purple yes
    violet affirmative
    
  3. Perl

    $ perl -pne 's/.*GRAPE=([^,]*).*FRUIT=([^,]*).*/\1 \2/' file 
    purple yes
    

    You might notice the above is slightly similar to the sed one :). Alternatively:

    $ perl -lne '@f=(/(?:(?<=GRAPE=)|(?<=FRUIT=))(.+?),/g); print "@f"' file 
    purple yes
    violet affirmative
    

    This uses , as a field separator and searches all fields:

    $ perl -F, -lane '@r=grep(s/.+?=//, grep(/GRAPE|FRUIT/,@F)); print "@r"' file 
    purple yes
    violet affirmative
    

    This one is shorter but adds a leading space to each line:

    $ perl -F, -lane 'print grep(s/.+?=/ /, grep(/GRAPE|FRUIT/,@F));' file 
     purple yes
     violet affirmative
    
  • Your grep can be simpler: grep -oP '(GRAPE|FRUIT)=\K.*?(?=,)' | paste -d" " - - – glenn jackman May 11 '15 at 13:46
  • @glennjackman yes indeed, much cleaner, thanks! – terdon May 11 '15 at 13:49
2

Using Awk:

awk -v RS="," -F= '/GRAPE/||/FRUIT/ {printf "%s ", $2}'

Changes the record separator from a new line to , and the field separator from a space to a =, then match lines that contain the pattern GRAPE or FRUIT and print the second matching field on the same line separated by a space. Result:

purple yes 
  • @patronizing_bofh If you want to load more patterns and control the output, you're better off saving matches as variables and printing them at the end: awk -v RS="," -F= '/GRAPE/{a=$2}; /FRUIT/{b=$2} END{print a,b"\n"}' – jasonwryan May 9 '15 at 7:24
  • @patronizing_bofh Please edit that in to your question: formatting in comments in sub par and it is difficult to parse. – jasonwryan May 10 '15 at 0:49

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.