I've been fiddling with this for a bit, working with smaller pieces intended for a shell pipeline.
It was complicated by the fact that each of the older OSes I tested on had a different representation for the app's pathname in the Dock plist:
Snow Lep :
"/Applications/Utilities/Activity Monitor.app/"
Mtn Lion :
"file://localhost/Applications/Utilities/Activity%20Monitor.app/"
Sierra :
"file:///Applications/Utilities/Activity%20Monitor.app/"
I think it's quite possible that other forms may be used by other OS versions.
Here are the shell functions I have so far. It's all in 'bash', and I didn't test in 'zsh', so test before actually using.
Code:
function dock_apps ()
{
local PLIST="${1:-com.apple.dock}"
echo >&2 " # -- $PLIST"
defaults read "$PLIST" persistent-apps \
| grep CFURLString\" \
| tr -d "\";" \
| awk '{ print $3,$4,$5 }'
}
# The $3,$4,$5 is for Snow Leopard's com.apple.dock data.
# It's needed because app pathnames may contain blanks,
# such as "Disk Utility" or "Address Book".
# Fields $4,$5 etc. are empty if pathname is URL-encoded,
# so there's no ill effect on Mtn Lion or Sierra data.
function dock_fix ()
{
awk '{ sub( /file:\/\/localhost/, "" ); print }' \
| awk '{ sub( /file:\/\//, "" ); print }'
}
# sub() operates on entire line if no src string given.
# urldecode() is from:
# https://stackoverflow.com/questions/6250698/how-to-decode-url-encoded-string-in-shell
function urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
function dock_eval ()
{
for (( NN=0; 1; ++NN ))
do
read ITEM || break;
CLEAN=$( urldecode "$ITEM" );
OK="del";
[[ -d "$CLEAN" ]] && OK=" ok";
echo " $OK $NN $CLEAN";
done
}
These are designed to be used in a pipeline, with an optional arg to 'dock_apps' that designates a plist file different from the default "com.apple.dock".
One thing I did right away was to duplicate the existing plist in the ~/Library/Preferences dir and name it "FAKE.dock.plist", so I could work on a plist without inadvertantly mangling my Dock configuration. None of the functions so far do any writing to any plist, so they should be safe to use on the real Dock plist. If you're unsure, make your own "FAKE.dock.plist".
Example usage:
Code:
dock_apps | dock_fix | dock_eval
The output will be a series of lines starting with either "ok" if the Dock icon is ok, or "del" if not. Then there's a number, which is the zero-based index (position) in the persistent-apps array. That's the datum that PlistBuddy will need. Finally, there's the pathname of the app, which is mainly for humans.
Example output lines:
Code:
ok 6 /Applications/Utilities/Disk Utility.app/
del 7 /Applications/Mission Control.app/
This is from a run on Snow Leopard using a plist file copied from Sierra, which is why there's no "Mission Control" app.
So far, all this does is evaluate whether a Dock icon refers to a valid app bundle or not (the -d test in dock_eval). It doesn't make any changes to the Dock's plist yet.
Below is the function that converts dock_eval's output into a series of PlistBuddy commands, intended to be piped to that process's stdin. I haven't started testing it on a live Dock yet, because it will take some time to create a test user account where I can easily put app references into the Dock and then break them. I'll probably setup a couple of apps, say "Mole.app" and "Groundhog.app", and move them between two dirs, so they disappear from their original pathname dir but reappear in another.
Code:
function dock_buddy ()
{
sort -k 2,2nr \
| awk ' ( $1 == "del" ) {
print "delete persistent-apps:" $2 }
END { print "save"; print "exit"; } '
}
# Example usage
dock_apps | dock_fix | dock_eval | dock_buddy
The dock_buddy output omits all the "ok" lines and app names, because only the index is significant for PlistBuddy. Also note that it starts deleting from the last entry and works down. This ensures that index positions are stable while the deletions are happening.
If run on a Dock where all the icons are valid, the only output should be this:
Code:
# -- com.apple.dock
save
exit
The first line is output from dock_apps stderr, so isn't part of the subsequent pipeline.