3

Let's assume I want to add this string to a preference domain:

a'b"c

Can I accomplish that with the defaults command?

These won't work (shell waits for further input):

defaults write testdomain testkey a'b"c
defaults write testdomain testkey 'a\'b"c'

Nor does this (error "Could not parse: a'b"c. Try single-quoting it."):

defaults write testdomain testkey "a'b\"c"
2
  • Is there a practical problem you are trying to solve here, with a real plist?
    – nohillside
    Jun 11, 2022 at 21:22
  • @nohillside Yes. I am planning to add a "generate defaults command" command to my Prefs Editor (apps.tempel.org). So I need to make a generic function for generating these arguments for any possible value in a plist, including dicts and arrays (which won't fully work, because once it's an array/dict, any written value will become a string type, it seems). Jun 11, 2022 at 22:39

2 Answers 2

9

This gets messy, because there are two layers of quote/escape interpretation (first by the shell, then by defaults), and they have somewhat different syntax rules. The short answer is to use "'a\\'b\"c'". Here's a demo:

$ defaults write testdomain testkey "'a\\'b\"c'"
$ defaults read testdomain testkey 
a'b"c

To figure out how to quote/escape a string like this, it's easiest to work backward through the layers: start with the final string you want to store, then figure out how to quote/escape that for defaults, then take the result of that and figure out how to quote/escape it for the shell.

So, we start with the final string to store:

a'b"c

defaults likes complex strings to be quoted, and any of the same type of quote within the string need to be escaped with a backslash. So here's that string with defaults-compatible quoting:

'a\'b"c'

Now we have to figure out how to get the shell to pass that to defaults as an argument. Shell syntax doesn't allow single-quotes in a single-quoted string (even if they're escaped), so double-quoting it will be simpler. Within double-quotes, single-quotes don't require any special treatment, but double-quotes and escapes both need to be escaped. So you can use this:

"'a\\'b\"c'"

Another possibility would be not shell-quote the string at all, but to individually escape all of the quotes and escapes in it:

\'a\\\'b\"c\'

(The triple-escape there is an escaped escape, followed by an escape for the single-quote. Of course there's a relevant xkcd.)

2
  • As I wrote in other comments, I'm trying to generically convert any CFType value into a string for the defaults command. Now, if I apply your rules to a dictionary, then it won't work any more. Example: defaults write t1 d '{v=1;}' works but when I double quote it (defaults write t1 d "'{v=1;}'"), the entered value become a string. Apparently the shell processes the single quotes. So, how would I add the value a'b"c inside a dict? Or shall I make a separate question for this? Jun 11, 2022 at 23:09
  • 1
    Nevermind - I've figured it out. I must not single-quote the entire dict but only the values inside. This arg works: "{v='a\\'b\"c';}". Jun 11, 2022 at 23:17
3

You new enough to replace " with \". However, you need to do this again and also replace \ with \\. So

a'b"c

becomes

"a'b\"c"

which becomes

"\"a'b\\\"c\""

after two passes. From the command line, this can be tested by entering the following command.

eval echo "\"a'b\\\"c\""

This command produces the following output.

a'b"c

In your case, you would enter the following command.

defaults write testdomain testkey "\"a'b\\\"c\""

Which can be verified with the following command.

defaults read testdomain testkey

I actually tested this answer by using com.apple.Finder instead of testdomain.

2
  • Good explanation. However, Gordon gave me even more details on the escaping (I need to write a generic function for converting strings into escaped shell arguments for my free tool "Prefs Editor"), so I'm awarding him. But then, you both already got more points than you'll ever spend :) Jun 11, 2022 at 22:35
  • 3
    The points themselves have no real value. I answer questions here at Ask Different to give back for information I get from other StackExchange sites. You should choose whichever post you feel best answers your question. Jun 11, 2022 at 22:51

You must log in to answer this question.

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