hello,
i’ve been working with .plist files lately and needed to both add and delete a ‘dict’ entry nested in an array. needless to say, i’ve found little documentation on doing this and while i’ve found that adding with ‘/usr/bin/defaults’ works pretty well, i cannot seem to find an array entry and delete it. this may be just my inability to find the proper documentation.
in my search i discovered ‘PlistBuddy’, a mysterious program that is installed when ‘Automatic Updates’ are perfomed on a Macintosh. i’ve written a test script to find ‘PlistBuddy’ and use it to find an array entry and delete it. you can often find PlistBuddy installed in weird places like:
/Library/Receipts/AdditionalEssentials.pkg/Contents/Resources/PlistBuddy
i realize that the use could just ‘drag’ an icon off the Dock, and that this program is fairly useless. it’s real intention is as an example for anyone who may need the same kind of functionality i do. i have not found anything like it out on the web. i’ve tried to do my best to comment the code, but i’m certainly willing to answer any questions you may have.
here’s the code:
(* removeDockItem -- pulls your Dock items into a list and allows you to delete a single item.
This is more a study on how to find and use PlistBuddy to remove items from a .plist file *)
(* Our properties. We need to set myPlistBuddy to something so that we can test
the variable after our 'Try' block. The 'myPersistent' variables refer to
'arrays' in our .plist file. You can check these with the Property List Editor,
which is available when you install the Developer Tools (Xcode). The global
is so that we can always refer to the App or Other that the user picks to delete,
and give good feedback *)
property myPlistBuddy : "no"
property myPersistentApps : "persistent-apps"
property myPersistentOthers : "persistent-others"
global myRm
(* This try block tests for PlistBuddy. If PlistBuddy does not exist, we do _not_
want our program to run. Note that the 'sed' command effectively
limits us to the _first_ instance of PlistBuddy that is found by
'locate'. In my experience most users have several of these on
their machines. *)
try
set myPlistBuddy to (do shell script "/usr/bin/locate PlistBuddy | sed 2,10000d")
end try
if myPlistBuddy is "no" then
display dialog "Cannot find PlistBuddy on this computer."
else
doMain()
end if
(* If our 'Try' block worked, we can get into the main subroutine. We'll let
the user pick a category of alias to delete from the Dock, and use getArray()
with the proper parameters. We could probably subroutine the deletion as well,
but it's just one line, so no big savings. *)
on doMain()
set myButton to button returned of (display dialog "What would you like to remove from the Dock?" buttons {"Application", "Other", "Cancel"} default button "Application")
if myButton is "Application" then
set myDockApp to (getArray(myPersistentApps))
if myDockApp ≥ 0 then
do shell script myPlistBuddy & " -c \"delete " & myPersistentApps & ":" & myDockApp & " dict\" ~/Library/Preferences/com.apple.dock.plist"
respawnDock()
display dialog myRm & " has been deleted from the Dock"
end if
else if myButton is "Other" then
set myDockOther to (getArray(myPersistentOthers))
if myDockOther ≥ 0 then
do shell script myPlistBuddy & " -c \"delete " & myPersistentOthers & ":" & myDockOther & " dict\" ~/Library/Preferences/com.apple.dock.plist"
respawnDock()
display dialog myRm & " has been deleted from the Dock"
end if
end if
end doMain
(* getArray() is where most of the work is done. Regardless of the array we are
working with, it does the same thing. Our 'Repeat' block allows for an 'unlimited'
(heh!) number of Dock items, since we know that the text, "Does Not Exist" will be
returned for non-existant elements. We have an extra test for the "Dashboard",
because for some reason this item is not named like the others. We make up for this
to avoid confusing the user. *)
on getArray(a)
set myArray to {missing value}
set x to 0
repeat while (do shell script myPlistBuddy & " -c \"print " & a & ":" & x & "\" ~/Library/Preferences/com.apple.dock.plist") does not contain "Does Not Exist"
if x is 0 then
if (do shell script myPlistBuddy & " -c \"print " & a & ":" & x & ":tile-data:file-label" & "\" ~/Library/Preferences/com.apple.dock.plist") is "" and (do shell script myPlistBuddy & " -c \"print " & a & ":" & x & ":tile-type" & "\" ~/Library/Preferences/com.apple.dock.plist") is "dashboard-tile" then
set myArray to "Dashboard" as list
else
set myArray to (do shell script myPlistBuddy & " -c \"print " & a & ":" & x & ":tile-data:file-label" & "\" ~/Library/Preferences/com.apple.dock.plist") as list
end if
else
if ((do shell script myPlistBuddy & " -c \"print " & a & ":" & x & ":tile-data:file-label" & "\" ~/Library/Preferences/com.apple.dock.plist") is "") and ((do shell script myPlistBuddy & " -c \"print " & a & ":" & x & ":tile-type" & "\" ~/Library/Preferences/com.apple.dock.plist") is "dashboard-tile") then
set myArray to (myArray & "Dashboard")
else
set myArray to (myArray & (do shell script myPlistBuddy & " -c \"print " & a & ":" & x & ":tile-data:file-label" & "\" ~/Library/Preferences/com.apple.dock.plist"))
end if
end if
set x to (x + 1)
end repeat
set myRm to (choose from list myArray) as string
set y to 1
set z to -1
-- This little 'Repeat' block searches the list we created above for the users selection.
repeat while y ≤ length of myArray
--display dialog item y of myArray
if quoted form of item y of myArray is quoted form of myRm then
set z to y
end if
set y to (y + 1)
end repeat
-- we return 'z - 1' because our array elements start at '0' but our list starts at '1'.
if z ≥ 0 then
return (z - 1)
else
return z
end if
end getArray
-- respawnDock() is code i took from a Qwerty Denzel post. Thanks Qwerty!
on respawnDock()
tell application "Dock"
quit
repeat
try
activate
exit repeat
end try
end repeat
end tell
end respawnDock
NOTE: i’ve only tested this with 10.4.7. results with other versions of OS X would be informative.