How to get a value from a plist file using the defaults command?

I have this plist file:



<?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>Dictionary1</key>
          <dict>
                    <key>Dictionary2</key>
                    <dict>
                              <key>Dictionary3-auto</key>
                              <dict>
                                        <key>Number1</key>
                                        <integer>1</integer>
                              </dict>
                    </dict>
          </dict>
</dict>
</plist>


User uploaded file

and I wonder how I can get a value for the Number1 key using Terminal. How should the "defaults read" command look like?

OS X Mavericks (10.9.2)

Posted on May 6, 2014 5:48 AM

Reply
5 replies

May 7, 2014 4:45 AM in response to MrHoffman

Hello


If PlistBuddy is not up to date to support recent preferences caching, here's a rubycocoa script using defaults(1) for reading plist and retrieve value at given key path. Key path is in the form as used in PlistBuddy except for using / instead of :.


E.g.,


./plist_read.rb '/Dictionary1/Dictionary2/Dictionary3-auto/Array1/0/String1' a.plist



# plist_read.rb


#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -w
require 'osx/cocoa'
include OSX

def dict_get_value_at_path(dict, keypath)
    v, p = dict, ''
    keypath.split('/').select {|a| a.length > 0}.each do |k|
        p << '/' + k
        if v.className == 'NSCFDictionary' && v.key?(k)
            v = v[k]
        elsif v.className == 'NSCFArray' && k =~ /^\d+$/ && v[k.to_i]
            v = v[k.to_i]
        else
            $stderr.puts '%s: No such key path.' % p
            return nil
        end
    end
    return v
end

raise ArgumentError, %Q[Usage: #{File.basename($0)} keypath plist] unless ARGV.length == 2
keypath, infile = ARGV

s = %x[defaults read "#{File.expand_path(infile).chomp('.plist')}"]
exit $? >> 8 if s == ''

plist = s.to_ns.propertyList
exit 1 unless plist

value = dict_get_value_at_path(plist, keypath)
exit 2 unless value
puts value



# test.sh (plist_read.rb is assumed to be saved in ~/desktop)


#!/bin/bash

cd ~/desktop

cat <<'EOF' > org.my.domain.plist
<?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>Dictionary1</key>
    <dict>
        <key>Dictionary2</key>
        <dict>
            <key>Dictionary3-auto</key>
            <dict>
                <key>Number1</key>
                <integer>1</integer>
                <key>Boolean1</key>
                <true/>
                <key>Array1</key>
                <array>
                    <dict>
                        <key>String1</key>
                        <string>abc</string>
                    </dict>
                    <dict>
                        <key>String1</key>
                        <string>def</string>
                    </dict>
                </array>
            </dict>
        </dict>
    </dict>
</dict>
</plist>
EOF

./plist_read.rb '/Dictionary1/Dictionary2/Dictionary3-auto/Array1/0/String1' org.my.domain.plist



Just in case this might be of some use.

All the best,

H



PS. Here's another version of plist_read.rb using CFPreferencesAppSynchronize() which is not well-tested, for I'm using 10.6.8.


#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -w
require 'osx/cocoa'
include OSX

def dict_get_value_at_path(dict, keypath)
    v, p = dict, ''
    keypath.split('/').select {|a| a.length > 0}.each do |k|
        p << '/' + k
        if v.className == 'NSCFDictionary' && v.key?(k)
            v = v[k]
        elsif v.className == 'NSCFArray' && k =~ /^\d+$/ && v[k.to_i]
            v = v[k.to_i]
        else
            $stderr.puts '%s: No such key path.' % p
            return nil
        end
    end
    return v
end

raise ArgumentError, %Q[Usage: #{File.basename($0)} keypath plist] unless ARGV.length == 2
keypath, infile = ARGV

bundleid = File.basename(infile, '.plist')
b = CFPreferencesAppSynchronize(bundleid)
$stderr.puts 'CFPreferencesAppSynchronize() failed for %s' % bundleid unless b

plist = NSDictionary.dictionaryWithContentsOfFile(infile)
unless plist
    $stderr.puts '%s: Failed to get dictionary.' % infile
    exit 1
end

value = dict_get_value_at_path(plist, keypath)
exit 2 unless value
puts value

May 6, 2014 7:08 AM in response to Hiroto

I'd approach PlistBuddy with some caution. The only mechanism that's compatible with the preferences caching present in Mountain Lion 10.8 and Mavericks 10.9 is the defaults command or the CFPreferences methods, and not AFAIK PlistBuddy or the older Xcode stuff — this situation does seem rather crazy, and hopefully will (did?) get fixed. (If you're just tossing data around via a plist and not changing and not caching it within an app, ignore this...)

#!/bin/bash

unset FOO

printf "Before: %s\n" $FOO

bar="MyValueToSave"

defaults write ~/Desktop/MyScriptPrefs MyKey $bar

export FOO=$(defaults read ~/Desktop/MyScriptPrefs MyKey)

printf "After: %s\n" $FOO


More info on the script.


The PrefsEdit GUI tool might interest some readers.

This thread has been closed by the system or the community team. You may vote for any posts you find helpful, or search the Community for additional answers.

How to get a value from a plist file using the defaults command?

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple Account.