0

maybe my question should answered in two ways but I hope it could be done with in one "sed":

I have the following lines, with different IDs:

ID1_TRINITY_DN120587_c0_g1::TRINITY_DN120587_c0_g1_i1::g.8298::m.8298

and I would like to get:

TRINITY_DN120587_c0_g1_i1[ID1]

1
sed -e '
   s/::/\n/;s//\n/
   s/^\([^_]*\)_.*\n\(.*\)\n.*/\2[\1]/
   ;#  |--1---|      |-2-|
' ID.data

Place the markers around the ID string and grab the portion before the first _ and replace the whole line with these values. Output:

TRINITY_DN120587_c0_g1_i1[ID1]

Explanation

              ID1_TRINITY_DN120587_c0_g1::TRINITY_DN120587_c0_g1_i1::g.8298::m.8298
              |-|                         |-----------------------|

You said you wanted the ID extracted which is lying between the 1st and 2nd occurrences of ::

Step-1: Place a marker (usually a \n) around the region of interest:

       s/::/\n/;s//\n/

   This is how the pattern space looks after the above tranformation

              ID1_TRINITY_DN120587_c0_g1\nTRINITY_DN120587_c0_g1_i1\ng.8298::m.8298

Step-2: Extract the ID which is the thing in between the two \ns as well as the string to the left of the 1st occurrence of _

                    s/^\([^_]*\)_.*\n\(.*\)\n.*/\2[\1]/
                    ;#  |------|      |---|
                    ;#     \1           \2

   [^_]       => matches any char but an underscore

   [^_]*      => matches 0 or more non underscore char(s)

   \([^_]*\)  => store what was matched into a memory, recallable as \1

   ^\([^_]*\) => anchor your matching from the start of the string

   .*\n       => go upto to the rightmost \n you can see in the string

   \n\(.*\)\n => Ooops!! we see another \n, hence we need to backtrack to
                 the previous \n position and from there start moving right again
                 and stop at the rightmost \n. Whatever is between these positions
                 is the string ID and is recallable as \2. Since the \ns fall outside
                 the \(...\), hence they wouldn't be stored in \2.

   .*         => This is a catchall that we stroll to the end of the string after
                 starting from the rightmost \n position and do nothing with it.

 So our regex engine has matched against the input string it was given in
 the pattern space and was able to store in two memory locations the data
 it was able to gather, viz.: \1 => stores the string portion which is in
 between the beginning of the pattern space and the 1st occurrence of the
 underscore.

 \2 => store the string portion which is in between the 1st and 2nd
       occurrences of :: in the pattern space.

                      \1 = ID1
                      \2 = TRINITY_DN120587_c0_g1_i1

 Now comes the replacement part. Remember that the regex engine was able to scan
 the whole of pattern space from beginning till end, hence the replacement
 will effect the whole of the pattern space.

 \2[\1] => We replace the matched portion of the pattern space (in our case it
           happens to be the entire string) with what has been stored in
           the memory \2 literal [ memory \1 literal ]
           leading to what we see below:

                  TRINITY_DN120587_c0_g1_i1[ID1]

In other words, you have just managed to turn the pattern space from:

              ID1_TRINITY_DN120587_c0_g1::TRINITY_DN120587_c0_g1_i1::g.8298::m.8298

into the following:

                  TRINITY_DN120587_c0_g1_i1[ID1]
  • This work perfect, could you explain it a little bit more in detail? especially how you used the |--1---| |-2-| – gusa10 Jul 5 '17 at 22:07
  • Pls. see the updated answer section. – user218374 Jul 6 '17 at 4:27
0

awk solution:

awk -F'::' '{ print $2"[" substr($1,1,index($1,"_")-1) "]"}' file

The output:

TRINITY_DN120587_c0_g1_i1[ID1]

  • -F'::' - field separator

  • substr($1,1,index($1,"_")-1) - extracting substring from the 1st field starting from the 1st position till the first occurrence of _ (i.e. ID1)

  • oh, that work perfect but I am still having deleted the lines that have not the same name as the example i provided – gusa10 Jul 5 '17 at 22:02
  • @gusa10, the lines that have not the same name - you could post that kind of lines – RomanPerekhrest Jul 5 '17 at 22:05
0

I am assuming here that your pattern will stay the same, this single sed solution should work.

sed -n "s/^\([^_]*\)_[^:]*::\([^:]*\)::.*/\2\[\1\]/p" filename

Output for example input :

TRINITY_DN120587_c0_g1_i1[ID1]

Explanation: Start from the beginning of line, match the content till the first underscore [^_]* and store it in first group, then match second group between the first and second double colon [^:]*. Replace this line and match with the desired output format, p prints the modified line.

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.