1

i have something to accomplish. i need to replace all occurrence of & inside or between <ex> </ex> to #. actual example below:

a & b & c <ex> a & b & c </ex> a & b & c

again, i need to replace all occurrence of & inside <ex> and before </ex>

expected output:

a & b & c <ex> a # b # c </ex> a & b & c

please post the explanation also on how you guys managed to make it.

EDIT#1

Please only provide me with a sed solution since I'm going to run this on an AS400 system and lack the ability to install Perl or any other interpreter.

0

Maybe this could help, if you have more than one occurence in the line, not nesting:

#cat plop
>a & b & c <ex> a & b & c </ex> a & b & c <ex> a & b & c </ex> a & b & c

#cat plop |sed -e :1 -e 's@\(<ex>[^(</ex>)]*\)&\(.*</ex>\)@\1+\2@;t1'     
>a & b & c <ex> a + b + c </ex> a & b & c <ex> a + b + c </ex> a & b & c
2

Another perl command,

$ perl -pe 's/&(?=(?:(?!<ex>|<\/ex>).)*<\/ex>)/#/g' file
a & b & c <ex> a # b # c </ex> a & b & c

Before explaining the above command, i'm the going to explain what actually a negative lookahead and positive lookahead will do.

In a regex (?=...) means a positive lookahead. lookarounds(ie; positive and negative lookahead,positive and nagative lookbehinds) would does a zero width match. That is, it won't match any character. Normally positive and negative lookaheads are used for condition checking purpose. And also (?:...) are called non-capturing groups. That is, pattern inside the non-capturing group will do a matching operation only. It won't capture any character.

  • (?!<ex>|<\/ex>) Impossible to match the strings <ex> or </ex>.
  • (?:(?!<ex>|<\/ex>).) What it does actually means, it first looks for the following three characters or four characters and ensures that the following three or four characters must not be <ex> or </ex>. It matches the following character . only if this condition is satisfied.
  • (?:(?!<ex>|<\/ex>).)* It do the above step, zero or more times until the string <ex> or </ex> is detected. Once it finds any of one these two strings, suddenly it stops matching the following characters.

  • (?:(?!<ex>|<\/ex>).)*<\/ex> Matches the following </ex> string also. This whole was given into the lookahead.

  • &(?=(?:(?!<ex>|<\/ex>).)*<\/ex>) Finally it matches the character & only if it is followed by the characters which satisfies the above mentioned condition. ie, & must be followed by any character not of <ex> or </ex> zero or more times followed by a closing tag </ex>

  • Can you explain how this works a bit? – slm Aug 28 '14 at 15:16
  • sure, give me 5 mins.. – Avinash Raj Aug 28 '14 at 15:17
  • Also on one of the other A's the OP said he needs the A to be in sed. – slm Aug 28 '14 at 15:23
  • @slm he wants the solution in sed only? If yes, then i'll delete mine. – Avinash Raj Aug 28 '14 at 15:25
  • Yes apparently the person asking the Q wanted sed, at least that's the comment he left for choroba. – slm Aug 28 '14 at 15:29
6

If there's only one occurrence of <ex>...</ex> per line:

sed -e :1 -e 's@\(<ex>.*\)&\(.*</ex>\)@\1#\2@;t1'

If there may be several occurrences and they don't nest (or they nest and you want to replace the & only in the deepest occurrences):

sed '
  s|_|_u|g        # replace all underscores with "_u"
  s|(|_o|g        # replace all open parentheses with "_o"
  s|)|_c|g        # replace all close parentheses with "_c"
  s|<ex>|(|g      # replace all open ex tags with "("
  s|</ex>|)|g     # replace all close ex tags with ")"

  :1              # a label

  s/\(([^()]*\)&\([^()]*)\)/\1#\2/g
                  # find:
                  #   an open parentheses, 
                  #   some non-parentheses chars (captured),
                  #   an ampersand, 
                  #   some non-parentheses chars (captured) and 
                  #   a close parentheses, 
                  # replace with
                  #   the first captured text, 
                  #   an octothorpe
                  #   the second captured text, 
                  # globally in the current record.

  t1              # if there was a successful replacement, goto label "1",
                  # else carry on

  s|(|<ex>|g      # restore open tags
  s|)|</ex>|g     # restore close tags
  s|_o|(|g        # restore open parentheses
  s|_c|)|g        # restore close parentheses
  s|_u|_|g        # restore underscores
'

If they may nest and you want to replace in the enclosing ones:

sed '
  s|_|_u|g;s|(|_o|g;s|)|_c|g
  s|<ex>|(|g;s|</ex>|)|g;:1
  s/\(([^()]*\)(\([^()]*\))\([^()]*)\)/\1_O\2_C\3/g;t1
  :2
  s/\(([^()]*\)&\([^()]*)\)/\1#\2/g;t2
  s|(|<ex>|g;s|)|</ex>|g
  s|_O|<ex>|g;s|_C|</ex>|g
  s|_o|(|g;s|_c|)|g;s|_u|_|g'
  • 1
    Just a curious. Why do you always use number as a label in your sed answer? – cuonglm Aug 28 '14 at 15:27
  • 1
    @Gnouc, probably reminds me of my first computer (Amstrad CPC464) where I did my first programming on locomotive basic. – Stéphane Chazelas Aug 28 '14 at 15:33
  • ill try this first thing in the morning tomorrow. also, could u give me an explanation about the syntax? i can understand the regular expression but the @s # and numbers and t1 im clueless. – kemaro Aug 28 '14 at 15:45
  • ps. im not a unix dev. i just use google always on every language that i need to use. my primary is cobol and java. – kemaro Aug 28 '14 at 15:46
  • @kemaro, I won't have time to provide with an explanation, but I've made it a community wiki answer, so maybe someone will step in and add an explanation. – Stéphane Chazelas Aug 28 '14 at 15:46
2

Perl (version 5.14 needed) to the rescue:

perl -pe 's%(<ex>.*?</ex>)% $1 =~ s/&/#/gr %eg'

In older versions, you have to be more verbose:

perl -pe 's%(<ex>.*?</ex>)% ($_x = $1) =~ s/&/#/g; $_x %eg'

Explanation: take everything between the <ex> tags into $1, and inside $1, replace & by #.

  • could u give me a sed answer for this? forgot to tell that i will use this in as400 platform and im afraid that only sed and grep basics will work. – kemaro Aug 28 '14 at 14:57
  • sorry. i cant install perl since my requirement is to just use what is already available in our system. do you have any idea on using sed? thanks a lot by the way for the awesome perl syntax and the explanation. – kemaro Aug 28 '14 at 15:13

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.