I have to be efficient so I can't use tools like jq which loads up a big binary executable. I just want to escape double quotes in a string so it's safe for JSON. This isn't good enough:

echo ' bad \"  string"' | sed 's/"/\\"/g' 

because it will escape double quotes that are already escaped. Is there a way to replace double quotes only if they are not already escaped?

closed as unclear what you're asking by muru, Sparhawk, msp9011, X Tian, Mr Shunz Jun 13 at 15:12

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    What environment is that you are in that considers a simpler executable jq to be bad? I would suggest using jq anyway for its myriad of functionalies – Inian Jun 6 at 6:00
  • I only need one piece of functionality and it's for another library so I don't need to load a big executable – Alexander Mills Jun 6 at 6:03
  • 1
    How about the other characters that would also need to be encoded for json (like control characters)? – Stéphane Chazelas Jun 6 at 6:51
  • 2
    How is sed any better than jq? On my system, sed is more than 5 times the size of jq. When you include the size of the libraries their are linked to, jq is slightly bigger, but that's mostly down to libm which will already be in memory anyway. jq takes 60% more time to start than GNU sed on my system though. – Stéphane Chazelas Jun 6 at 6:58
  • 1
    Is your application restricted by the speed at which external utilities can be launched? – Kusalananda Jun 6 at 7:30

Presumably you only want to escape " unless it preceded by \.

echo ' bad \"  string"' | sed -E 's/([^\]|^)"/\1\\"/g'


This will match ", but only if it's preceded by [^\], which is "any character except \" (or the start of the line ^). However, since this new character will be replaced itself, we need to capture it in a capturing group (), then replace it again with the match \1. In this example, I've used extended regular expressions with -E for simplicity.

  • upvoted, but yeah unfortunately it won't work in cases like this stackoverflow.com/questions/56471705/…, I need to add more logic to it – Alexander Mills Jun 6 at 6:06
  • I need to escape unescaped escapes or whatever lol – Alexander Mills Jun 6 at 6:07
  • @AlexanderMills Could you please edit your question with a sample input and output, including what should and should not be altered? – Sparhawk Jun 6 at 6:08
  • There's probably a few edge cases, basically need to produce a string that can surrounded by double quotes and not have any double quotes in it that aren't escaped, but if something is already escaped cannot double escape it. – Alexander Mills Jun 6 at 6:14
  • @AlexanderMills The "however JSON works" is what confuses me. If you could give explicit examples in your question, that would help us answer you! – Sparhawk Jun 6 at 6:15
$ echo ' bad \"  string"' | perl -pe 's/(?<!\\)"/\\\"/g'
 bad \"  string\"
$ echo ' bad \"  string" """""""""' | perl -pe 's/(?<!\\)"/\\\"/g'
 bad \"  string\" \"\"\"\"\"\"\"\"\"
$ echo ' bad \"  string" """"""""" \"' | perl -pe 's/(?<!\\)"/\\\"/g'
 bad \"  string\" \"\"\"\"\"\"\"\"\" \"

using negative lookbehind, you can achieve this. https://www.regular-expressions.info/lookaround.html

Not the answer you're looking for? Browse other questions tagged or ask your own question.