0

Given a string (or a file contains lines of text with similar strings) which contains double-quotes, some of which are preceded by a backslash, what is the optimal way with sed to replace the double-quotes which are not preceded by a backslash with some other character or string?

      original string    'Abc \"NN""xyz\"ddd"ee "ff" \"QQ\"'

      replace non backslash double-quote with percent symbol

           new string    'Abc \"NN%%xyz\"ddd%ee %ff% \"QQ\"'

The only way I have found so far to do this is to first replace the "backslash double-quote" combination with some unique character or string which is never found in the text and is different from the replacement character or string, then substitute the double quotes with the desired replacement (eg a backslash double-quote or for clarity in this example a percent sign), and then change the unique holding string back to its original backslash double-quote. Thus three invocations of sed are required for this method.

So is there a simpler way to do this with sed?

3
  • how is \\" to be handled? is the \" in that case not special, or ... ?
    – thrig
    Aug 24, 2022 at 22:07
  • Do you have a real world example to work with? Your example contains an odd number of double quotes, which is unusual.
    – Kusalananda
    Aug 24, 2022 at 22:14
  • You can do multiple substitutions with the same invocation of sed. I think sed -e 's/\\"/→/g' -e 's/"/%/g' -e 's/→/\\"/g' would work here.
    – frabjous
    Aug 24, 2022 at 22:19

3 Answers 3

2

Using GNU sed

$ sed -E ':a;s/(\\")([^\]*)"/\1\2%/;ta' input_file
'Abc \"NN%%xyz\"ddd%ee %ff% \"QQ\"'
2
  • 2
    Somewhat simpler: sed -E ':a; s/([^\])"/\1%/; ta' Aug 24, 2022 at 22:40
  • @GGB, The reason you need the "while loop" with :a and ta is that, if you do s/([^/])"/\1%/g, then the N" gets replaced and then matching resumes at "x where that double quote will not be replaced. The regex will go backwards over text that has already been consumed and replaced. Aug 24, 2022 at 22:50
1
sed -e 's/[\]"/%/g' -e 'y/"%/%"/' -e 's/"/\\"/g'

This first replaces each occurrence of \" with %. It then swaps all % for " and vice versa. Finally, all remaining double quotes are escaped.

If the string does not contain any % from the start, then the effect is that all un-escaped double quotes are replaced by %.

Alternatively, but using an extra character, @, that can't be in the data from the start:

sed -e 's/[\]"/@/g' -e 'y/"/%/' -e 's/@/\\"/g'

This variant shows a bit more cleanly what happens and is also what you propose in the question (only using a single invocation of sed though): We're "hiding" the escaped double quotes temporarily while we're turning the un-escaped double quotes into %. When done, we restore them.

0

Making use of extended regex feature (-E|-r) of the GNU sed stream editor.

The thing is to detect a quote character that sees an even number(zero too is even) of backslashes before it and then either a non backslash character or the beginning of line.

That quote is transformed into a percent symbol.

Version for double quotes.

sed -E -f - <<\eof file
  :a;s/((^|[^\])([\][\])*)"/\1%/g;ta
eof

Version for single quotes.

sed -E -f - <<\eof file
  :a;s/((^|[^\])([\][\])*)'/\1%/g;ta
eof

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .