32

Consider a preference plist with a dict that contains an array:

Let's create it:

defaults write org.my.test '{aDict = {anArray = ();};}'

Then read it back to see the structure better:

$defaults read org.my.test
{
    aDict = {
        anArray = (
        );
    };
}

Now, how do I add a value to anArray using the defaults write command?

I know that there is the -array-add type for adding values to an array, but how do I specify the key path to the array element?

I've tried this, but that doesn't work:

defaults write org.my.test aDict.anArray -array-add "a value"

In fact, I need to add a non-string type, so I also need to be able to specify the type, e.g. -bool YES.

(Note: I cannot use PlistBuddy nor plutil as this needs to affect live preferences)

1
  • Just bumped into this one myself. I wonder if you found any solution that also support cached preferences Commented Mar 4, 2020 at 11:19

4 Answers 4

9

Use plutil and your life will be better. Defaults doesn't support key paths.

> defaults write org.my.test '{aDict = {anArray = ();};}'

> defaults read org.my.test
{
    aDict =     {
        anArray =         (
        );
    };
}

> plutil -insert aDict.anArray.0 -bool YES ~/Library/Preferences/org.my.test.plist

> defaults read org.my.test
{
    aDict =     {
        anArray =         (
            1
        );
    };
}

I used defaults read just to prove that the expected inputs are the same, but you'll probably use plutil -p ~/Library/Preferences/org.my.test.plist to read the file instead if you start using plutil more.

Sign up to request clarification or add additional context in comments.

4 Comments

The problem with this solution is that it won't work with preferences files, because those are cached since 10.8 or so, hence modifying the files on disk will not update the cache accordingly. And I need to do this with preferences ("User Defaults").
@ThomasTempelmann do you know where the cache is? Thinking about deleting the cache to invalidate it if I can find it, so that it'll have to check the original file
@PatMyron It's cached in memory, by the daemon cfprefsd. You could force-quit it to flush its cache, but that's not a path I want to go down.
Completely false information above; "defaults doesn't support key paths". YES! defaults DOES access sub-keys. And it's easier than I thought. There's no reason to use plutil or other methods. It's unfortunate that there's a character limit here, and I can't post the file. It was about 1000 characters over the limit, but here's the most important line; # Save the subkey and value under the root key defaults write "$domain" "$root_key" -dict-add "$sub_key" "$value"
3

I adopted Thomas Tempelmann's answer and come up this snippet:

defaults export com.apple.symbolichotkeys - |
    # Save picture of screen as a file: Off
    plutil -replace AppleSymbolicHotKeys.28.enabled -bool NO -o - - |
    # Copy picture of screen to the clipboard: Off
    plutil -replace AppleSymbolicHotKeys.29.enabled -bool NO -o - - |
    # Save picture of selected area as a file: Off
    plutil -replace AppleSymbolicHotKeys.30.enabled -bool NO -o - - |
    # Copy picture of selected area to the clipboard: Off
    plutil -replace AppleSymbolicHotKeys.31.enabled -bool NO -o - - |
    # Screenshot and recording options: Off
    plutil -replace AppleSymbolicHotKeys.184.enabled -bool NO -o - - |
    defaults import com.apple.symbolichotkeys -

This snippet disables all screenshot-related keyboard shortcut. The -o - indicates plutil to pipe output to stdout and the following - indicates reading from stdin.

1 Comment

This should be the accepted answer. Works like a charm, and doesn't create temporary files. It does use plutil, but through defaults, so it is cfprefsd-compliant.
1

This expands on Thomas Tempelmann's idea and Liyan Chang's work and deals with cfprefsd by using the defaults command to re-write the plist which appears to work with cfprefsd: 1 2

defaults write org.my.test '{aDict = {anArray = ();};}'
defaults export org.my.test /tmp/foo.plist
plutil -insert aDict.anArray.0 -bool YES /tmp/foo.plist
defaults import org.my.test /tmp/foo.plist

Or you can do all of it on one line:

defaults write org.my.test "$(defaults export org.my.test - | plutil -insert aDict.anArray.0 -bool YES - -o -)"

Here it is expanded out and easier to follow:

> defaults write org.my.test '{aDict = {anArray = ();};}'

> defaults export org.my.test -
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aDict</key>
    <dict>
        <key>anArray</key>
        <array/>
    </dict>
</dict>
</plist>

> defaults export org.my.test /tmp/foo.plist

> plutil -insert aDict.anArray.0 -bool YES /tmp/foo.plist

> defaults import org.my.test /tmp/foo.plist

> defaults export org.my.test -                                   
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aDict</key>
    <dict>
        <key>anArray</key>
        <array>
            <true/>
        </array>
    </dict>
</dict>
</plist>
> 

Also instead of doing defaults write org.my.test '{aDict = {anArray = ();};}' which makes it hard to know the types you can do it as a plist like defaults write org.my.test '<dict><key>aDict</key><dict><key>anArray</key><array/></dict></dict> which makes it easier to see the types but much longer.

Comments

0

There may be a way by exporting the entire preferences set to XML, then modify that text to include the changes, then re-importing the XML into the preferences.

Basically, you'd use defaults export to create the XML output, then something like sed (see here for an example), then use defaults import to get it back into the prefs.

I have not figured out a generic method for this, so this is just a stub, but if you figure it out, please edit this answer, as I'll make it a "community wiki" entry, so that anyone can modify it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.