0

Why is this simple replacement with sed inside bash not working?

echo '[a](!)' | sed 's/[a](!)/[a]/'

It returns [a](!) instead of [a]. But why, given that only three characters need to be escaped in a sed replacement string?

If I account for the case that additional characters need to be replaced in the regex string and try

echo '[a](!)' | sed 's/\[a\]\(!\)/[a]/'

it is still not working.

Community
  • 1
  • 1
Drux
  • 11,992
  • 13
  • 66
  • 116

3 Answers3

3

The point is that [a] in the regex pattern does not match square brackets that form a bracket expression. Escape the first [ for it to be parsed as a literal [ symbol, and your replacement will work:

echo '[a](!)' | sed 's/\[a](!)/[a]/'
                       ^^

See this demo

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
1

sed uses BREs by default and EREs can be enabled by escaping individual ERE metacharaters or by using the -E argument. [ and ] are BRE metacharacters, ( and ) are ERE metacharacters. When you wrote:

echo '[a](!)' | sed 's/\[a\]\(!\)/[a]/'

you were turning the [ and ] BRE metacharacters into literals, which is good, but you were turning the literal ( and ) into ERE metacharacters, which is bad. This is what you were trying to do:

echo '[a](!)' | sed 's/\[a\](!)/[a]/'

which you'd probably really want to write using a capture group:

echo '[a](!)' | sed 's/\(\[a\]\)(!)/\1/'

to avoid duplicating [a] on both sides of the substitution. With EREs enabled using the -E argument that last would be:

echo '[a](!)' | sed -E 's/(\[a\])\(!\)/\1/'

Read the sed man page and a regexp tutorial.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • I see no point in capturing *known* pattern part for use in the replacement, that is redundant overhead. – Wiktor Stribiżew Aug 15 '16 at 22:55
  • The alternative is repeating the same text in the replacement section as you had in the search section which means if/when that text changes you have to change it in multiple places in the script which is fundamentally bad coding practice. – Ed Morton Aug 15 '16 at 23:33
-1

man echo tells that the command echo display a line of text. So [ and ( with their closing brackets are just text.

If you read man grep and type there /^\ *Character Classes and Bracket Expressions and /^\ *Basic vs Extended Regular Expressions you can read the difference. sed and other tools that use regex interprets this as Character Classes and Bracket Expressions.

You can try this

$ echo '[a](!)' | sed 's/(!)//'
John Goofy
  • 1,330
  • 1
  • 10
  • 20
  • No, I just read this in the man pages for giving a referenced answer and found this solution by myself. I just tried to give an answer instead of down voting. It would be nice if the questioners could only down vote. But maybe it is not a joke what people are talking about Jon Skeet. – John Goofy Aug 13 '16 at 21:27
  • No, I read this page, looked up for an answer in the man pages, what I always do and posted my answer. Sometimes some people are faster, sometimes people don't agree with this and like to say that. Some people think there are bad questions, some think there are bad answers and some think that every help is welcome, but some don't think that way. If you could explain what is wrong with my answer and why this answer doesn't work in some shell's or different versions of sed or is not portable, I could accept your down voting, because I woul have learned something, but you don't give a hint. – John Goofy Aug 13 '16 at 22:52
  • Which man pages you do recommend that point out the difference in characters that are used as literals and meta characters in different forms and that this is depending on the used tools? Give me input that enlighten me and stop saying that you don't like something.. – John Goofy Aug 13 '16 at 23:17
  • Once again, I have pointed to `man grep` and that there is written that literals and meta characters are not the same and that different tools handle this different, especially that `echo` don't now about this. This means typing something for an `echo`output is not the same like a regex. If you think it's better to google for "regexp tutorial" then reading man pages please write man pages, you can contact mailto:gvc@gnu.org – John Goofy Aug 14 '16 at 00:08
  • Exactly! That's what I've written. In other words `int i = 1;` is not the same like `char i[] = "1";` but it seems that 1 is 1. But `echo 1` is not `regex 1`. That's what I have pointed out by looking to the man pages. So why down voting if you have the same opinion? And why is your accepted answer not voted like the unaccepted answer? If you think that man pages or the info tool are not recommended, please share your knowledge and `mailto:gvc@gnu.org`, they are looking for people like you ;) Share knowledge, please stop saying this is not good or try out google. – John Goofy Aug 14 '16 at 02:57
  • I have very patiently tried to explain the issues with your answer and you act like you don't understand them. Hopefully you do understand and are just pretending not to for some reason but either way there's nothing more I can do. Good luck with your scripting. – Ed Morton Aug 14 '16 at 04:05