4

I have string which I would like to format. I would like to remove everything between second ; and second last ;.

Input

cellular organisms;Eukaryota;Opisthokonta;Metazoa;Eumetazoa;Bilateria;Protostomia;Ecdysozoa;Panarthropoda;Arthropoda;Mandibulata;Pancrustacea;Hexapoda;Insecta;Dicondylia;Pterygota;Neoptera;Endopterygota;Coleoptera;Polyphaga;Cucujiformia;Tenebrionoidea;Tenebrionidae;Tenebrionidae incertae sedis;Tribolium;Tribolium castaneum;  

Output

cellular organisms;Eukaryota;Tribolium castaneum;

I have tried using sed

sed 's/;[^;]*//' <<<"cellular organisms;Eukaryota;Opisthokonta;Metazoa;Eumetazoa;Bilateria;Protostomia;Ecdysozoa;Panarthropoda;Arthropoda;Mandibulata;Pancrustacea;Hexapoda;Insecta;Dicondylia;Pterygota;Neoptera;Endopterygota;Coleoptera;Polyphaga;Cucujiformia;Tenebrionoidea;Tenebrionidae;Tenebrionidae incertae sedis;Tribolium;Tribolium castaneum;"

produces

cellular organisms;Opisthokonta;Metazoa;Eumetazoa;Bilateria;Protostomia;Ecdysozoa;Panarthropoda;Arthropoda;Mandibulata;Pancrustacea;Hexapoda;Insecta;Dicondylia;Pterygota;Neoptera;Endopterygota;Coleoptera;Polyphaga;Cucujiformia;Tenebrionoidea;Tenebrionidae;Tenebrionidae incertae sedis;Tribolium;Tribolium castaneum;
5

You can do this easily with awk:

awk -F\; '{print $1 ";" $2 ";" $(NF-1) ";" $NF}'

This splits the input using ; (-F\;), and prints the first ($1), second ($2), second-to-last and last fields ($(NF-1) and $NF; NF contains the number of fields).

The following variant re-uses the specified field separator in the output:

awk -F\; '{print $1 FS $2 FS $(NF-1) FS $NF}'

Janis suggested an improved version using OFS too:

awk 'BEGIN{FS=OFS=";"} {print $1,$2,$(NF-1),$NF}'

or, if you want to keep the separator as another parameter:

awk -F\; 'BEGIN{OFS=FS} {print $1,$2,$(NF-1),$NF}'
  • Actually, $NF seems not to be used in the OP's sample data? Anyway. Another variant with awk can define the OFS as well, so that the print command gains a bit more legibility: awk 'BEGIN{FS=OFS=";"} {print $1,$2,$(NF-1),$NF}' – Janis Mar 22 '15 at 15:47
  • 1
    Yes, I included $NF to answer the question as stated, rather than the simpler version in the example data. Thanks for the OFS idea! – Stephen Kitt Mar 22 '15 at 15:49
3

A few Perl approaches. In all cases, I have run this command to populate $string:

string="cellular organisms;Eukaryota;Opisthokonta;Metazoa;Eumetazoa;Bilateria;Protostomia;Ecdysozoa;Panarthropoda;Arthropoda;Mandibulata;Pancrustacea;Hexapoda;Insecta;Dicondylia;Pterygota;Neoptera;Endopterygota;Coleoptera;Polyphaga;Cucujiformia;Tenebrionoidea;Tenebrionidae;Tenebrionidae incertae sedis;Tribolium;Tribolium castaneum;"

 

$ perl -F';' -lane '$"=";"; print "@F[0,1,$#F-1];"' file 
cellular organisms;Eukaryota;Tribolium castaneum;

or

$ perl -F';' -lane 'print "$F[0];$F[1];$F[$#F];"' <<<"$string"
cellular organisms;Eukaryota;Tribolium castaneum;

or

$ perl -F';' -lane 'print join(";", @F[0,1,$#F-1]) . ";"' <<<"$string"
cellular organisms;Eukaryota;Tribolium castaneum;
3

Another sed approach:

sed 's/\(^[^;]*\;[^;]*\).*\(\;[^;]*\;$\)/\1\2/'

Output: cellular organisms;Eukaryota;Tribolium castaneum;

2

Do it with sed

sed 's/\(\([^;]*\;\)\{2\}\).*\;\([^;]*;\)/\1\3/'
0
sed -n 's/\(;[^;]*;\).*\(;.*;\)/\1\2/p' <infile

...the above would only print anything at all for input lines which match at least 4 ; chars, and for those would print only the content which comes before and after and including the second and second-to last occurring ; chars respectively.

More easily, though, if you first verify that you have at least fourm you can do:

 sed -n '/\(;.*\)\{4\}/s/[^;]*//3p' <infile

Which first assures that the line matches at least 4 ;, then removes the third occurring sequence of zero-or-more not-semi-colon chars. The test is necessary because the s///ubstitution could apply to any line matching at least 2 semi-colons - so the test assures there is a second-to-last ; semi-colon to before attempting the strip.

Both of the above suggested solutions print only those lines against which the substitution is successful. You could print all lines by instead removing the -n and p commands - the substitutions will still be attempted and only their results printed for lines against which they match, but will print all else anyway.

Of course if you're sure of your input, all you need is:

sed 's/[^;]*//3' <infile
  • @don_crissti - hmm, you're right . Duh - the first one will clear all of that. But the other two? Oh. Not just a field but all of the fields,. The first one does that - the other two don't. The question though asks to remove everything between ;; 2--2, literally ;; is correct. – mikeserv Mar 22 '15 at 23:43
0

Through python3:

#!/bin/python3
import sys
fil = sys.argv[1]
with open(fil) as f:
    for line in f:
        m = line.strip().split(';')
        print(';'.join(m[:2]+m[-2:]))

Save the above in a file called script.py and then run using:

python3 script.py file

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.