I have a string and I need to add #, in the beginning,i.e. convert [ -n "$ID" -a "$ID" -le 200 ] && return to #[ -n "$ID" -a "$ID" -le 200 ] && return. I can use below command:

echo '[ -n "$ID" -a "$ID" -le 200 ] && return'| sed -n -e 's/\[ -n "$ID" -a "$ID" -le 200 \] && return/#&/p'

It works. Now I have two questions, regarding E, flag.

  1. For the string [ -n "$ID" -a "$ID" -le 200 ], if I escape brackets, it does not work; however, it works when I do not escape them i.e.

    sed -n -E 's/[ -n "$ID" -a "$ID" -le 200 ]/#&/p'

    works while

     sed -n -E 's/\[ -n "$ID" -a "$ID" -le 200 \]/#&/p'

    doesn't work.

  2. For, the full string [ -n "$ID" -a "$ID" -le 200 ] && return, it gives me wrong answer, when I do not escape them:

    echo '[ -n "$ID" -a "$ID" -le 200 ] && return'| sed -n -E 's/[ -n "$ID" -a "$ID" -le 200 ] && return/#&/p'

    It gives me output:

     [ -n "$ID" -a "$ID" -le 200 #] && return

I want to know how it is working.

  • 6
    The [ and ] must be escaped in both BRE and ERE (otherwise they define a range expression). As to why you get no output when escaping them and using ERE, I think it's a difference in how $ is treated between BRE and ERE when it's not the final character of a pattern - see for example Bash sed replace double dollar sign $$ extended regular expressions – steeldriver Sep 20 '19 at 12:29
  • 2
    sed's -E and -e options are two completely different and unrelated things. they are not alternate versions of each other. -e tells sed that the next argument is a script to be run. -E tells sed to use extended regular expressions (ERE) instead of sed's default of basic regular expressions (BRE). – cas Sep 20 '19 at 13:02
  • You can drop the "-e", it's only if you have separate expressions. The -E option would work if you escaped the "$"'s. – A.Danischewski Sep 21 '19 at 0:24
sed -n -E 's/[ -n "$ID" -a "$ID" -le 200 ]/#&/p'

Does not work the way you expect it to work, it's more of a coincidence: [...] is a collection of characters that match. Inside that collection you have a range -n from whitespace (0x20) to n (0x6E), which can include [ (0x5B), depending on your locale settings. So the collection matches the first char. See what happens when you take #&___ as the replacement... not your intention, I guess?

And that's the reason for your full case to fail: The matching character for the collection is the ] because it is followed by the rest of the pattern, that's why the # is inserted there.

The problem with the extended regular expression is like @steeldriver assumed: The $ anchors the pattern:

A ( '$' ) outside a bracket expression shall anchor the expression or subexpression it ends to the end of a string; such an expression or subexpression can match only a sequence ending at the last character of a string. For example, the EREs "ef$" and "(ef$)" match "ef" in the string "abcdef", but fail to match in the string "cdefab", and the ERE "e$f" is valid, but can never match because the 'f' prevents the expression "e$" from matching ending at the last character.

So in ERE, a literal $ needs to be escaped, while in BRE it only needs to be escaped when being the last character of the pattern.

Also note that -e marks the next argument as a script and is optional if there is only one script, while -E is a switch. Dropping -e for '-E` only works if there is only one script because it was superfluous anyhow.

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.