app2dock
An exercise in using PlistBuddy.
# first rebuild the Launch Services database lsregister -kill -r -f -all system,local,user # Mac OS X 10.5 lsregister -kill -r -f -domain local -domain system -domain user lsregister -kill -r -f / # entire system set -xv eval lsregister -kill -r -f $(/bin/df -l | /usr/bin/sed -E -e 1d -e 's/^([^ ]+[ ]+)[^\/]+//' -e "s/^(.+)$/'\1'/" | /usr/bin/tr '\n' ' ') set +xv dockplistfile="${HOME}/Library/Preferences/com.apple.dock.plist" cp -ip "${dockplistfile}" "${dockplistfile}.orig" plutil -convert xml1 -o /dev/fd/1 "${dockplistfile}" | /usr/bin/pl plutil -convert xml1 -o ~/Desktop/com.apple.dock.plist "${dockplistfile}" open -e ~/Desktop/com.apple.dock.plist # get an example entry from $dockplistfile (last application in the Dock) # cf. http://codesnippets.joyent.com/posts/show/1814 num_apps_dock=$(/usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" | awk -F '=' '/CFURLString = /{print $2}' | wc -l) echo ${num_apps_dock} /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${num_apps_dock}'" "${dockplistfile}" #/usr/libexec/PlistBuddy -c "Print :persistent-apps:${num_apps_dock}" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${num_apps_dock}':tile-data:file-data:_CFURLString" "${dockplistfile}" open /bin/bash # we will first create an example entry step by step # note: persistent-apps is an array that stores dictionaries dockplistfile="${HOME}/Library/Preferences/com.apple.dock.plist" # get a free array index number (that is, the last + 1 array index) free_num_apps_dock=$(( 1 + $(/usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" | awk -F '=' '/CFURLString = /{print $2}' | wc -l) )) echo ${free_num_apps_dock} # add a dictionary to the persistent-apps array at array index ${free_num_apps_dock} /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}' dict" "${dockplistfile}" # print the dictionay in the persistent-apps array at array index ${free_num_apps_dock} /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" # add a dictionary named "tile-data" to the previously added dictionary /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data dict" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" # add the dictionary 'file-data' to the dictionary 'tile-data' /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data dict" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" # add key - value pairs to the file-data dictionary /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:thisIsAKey string 'this is a string value'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLString string '/path/to/app'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLStringType integer 0" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" # delete the dictionay in the persistent-apps array at array index ${free_num_apps_dock} /usr/libexec/PlistBuddy -c "Delete :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" # print all dictionaries of the persistent-apps array /usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" # short version dockplistfile="${HOME}/Library/Preferences/com.apple.dock.plist" free_num_apps_dock=$(( 1 + $(/usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" | awk -F '=' '/CFURLString = /{print $2}' | wc -l) )) echo ${free_num_apps_dock} /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:thisIsAKey string 'this is a string value'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLString string '/path/to/app'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLStringType integer 0" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Delete :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" :<<-'COMMENT' # example entry Dict { tile-data = Dict { file-data = Dict { thisIsAKey = this is a string value _CFURLString = /path/to/app _CFURLStringType = 0 } } } COMMENT #----------------------------------------------------------------------------- # first rebuild the Launch Services database lsregister -kill -r -f -all system,local,user # Mac OS X 10.5 lsregister -kill -r -f -domain local -domain system -domain user lsregister -kill -r -f / # entire system set -xv eval lsregister -kill -r -f $(/bin/df -l | /usr/bin/sed -E -e 1d -e 's/^([^ ]+[ ]+)[^\/]+//' -e "s/^(.+)$/'\1'/" | /usr/bin/tr '\n' ' ') set +xv # app2dock # allows case insensitive name of application as input # uses lsregister & egrep -i # lsregister /usr/bin/sudo /bin/ln -is $(/usr/bin/locate *lsregister | /usr/bin/head -n 1) /bin/lsregister unset -f app2dock function app2dock() { declare appname dockplistfile free_num_apps_dock path num index declare -a fullpaths dockplistfile="${HOME}/Library/Preferences/com.apple.dock.plist" for appname in "${@}"; do appname="${appname%/}" declare IFS=$'\n' # bug: does not work for file paths with embedded newlines \n fullpaths=( $(/bin/lsregister -dump | /usr/bin/egrep -i "path: +.*\/[^\/]*${appname}[^\/]*\.app$" | \ /usr/bin/sed -E 's/^[[:space:]]+path:[[:space:]]+//') ) if [[ ${#fullpaths[@]} -eq 0 ]]; then echo "No such application: ${appname}" continue elif [[ ${#fullpaths[@]} -eq 1 ]]; then path="${fullpaths[0]}" else echo for ((i=0; i < ${#fullpaths[@]}; i++)); do printf "\e[1m%+8s\e[m %s\n" "$[ ${i} +1 ]:" "${fullpaths[${i}]##*/}" # print basename only done echo read -e -p $'\e[1mChoose one of the numbers above\e[m: ' num echo # check if $num is a positive integer # do not allow leading zeros if [[ "${num//[[:digit:]]/}" != "" ]] || [[ "${num#"${num%%[!0]*}"}" != "${num}" ]]; then echo 'Wrong input!' continue fi index=$[ $num - 1 ] [[ ${index} -ge ${#fullpaths[@]} ]] && echo 'Number too high!' && continue path="${fullpaths[${index}]}" fi path="${path%/}" # cut off trailing slash if any [[ ! -d "${path}" ]] && echo "No such directory: ${path}" && continue # if $path does not end with .app ... [[ "${path}" == "${path%.app}" ]] && echo "Argument error: ${path}" && continue # escape single & double quotes in $path path="${path//\'/\'}" path="${path//\"/\\\"}" printf "%s\n" "$path" free_num_apps_dock=$(( 1 + $(/usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" | \ /usr/bin/awk -F '=' '/CFURLString = /{print $2}' | /usr/bin/wc -l) )) #echo ${free_num_apps_dock} /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLString string '${path}'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLStringType integer 0" "${dockplistfile}" #/usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" /usr/libexec/PlistBuddy -c save "${dockplistfile}" &>/dev/null done # for /usr/bin/killall -HUP Dock return 0 } # app2dock_fp # only allows full file path to application as input function app2dock_fp() { declare dockplistfile free_num_apps_dock path dockplistfile="${HOME}/Library/Preferences/com.apple.dock.plist" for path in "${@}"; do path="${path%/}" if [[ ! -d "${path}" ]] || [[ "${path}" == "${path%.app}" ]]; then echo "Argument error: ${path}"; continue; fi free_num_apps_dock=$(( 1 + $(/usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" | \ /usr/bin/awk -F '=' '/CFURLString = /{print $2}' | /usr/bin/wc -l) )) #echo ${free_num_apps_dock} # escape single & double quotes in $path path="${path//\'/\'}" path="${path//\"/\\\"}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLString string '${path}'" "${dockplistfile}" /usr/libexec/PlistBuddy -c "Add :persistent-apps:'${free_num_apps_dock}':tile-data:file-data:_CFURLStringType integer 0" "${dockplistfile}" #/usr/libexec/PlistBuddy -c "Print :persistent-apps:'${free_num_apps_dock}'" "${dockplistfile}" /usr/libexec/PlistBuddy -c save "${dockplistfile}" &>/dev/null done /usr/bin/killall -HUP Dock return 0 } # remove_app_from_dock # allows case insensitive name of application as input function remove_app_from_dock() { declare appname dockplistfile nums="" dockplistfile="${HOME}/Library/Preferences/com.apple.dock.plist" for appname in "${@}"; do appname="${appname%/}" nums="" nums=$(/usr/libexec/PlistBuddy -c "Print :persistent-apps" "${dockplistfile}" | /usr/bin/awk -F '=' '/CFURLString = /{print $2}' | \ /usr/bin/nl | /usr/bin/egrep -i "${appname}" | /usr/bin/awk '{print $1}' | /usr/bin/sort -rn) if [[ -z "${nums}" ]]; then echo "No application: ${appname} found in the Dock."; continue; fi for num in ${nums}; do /usr/libexec/PlistBuddy -c "Delete :persistent-apps:'${num}'" "${dockplistfile}" done # test #for num in ${nums}; do # /usr/libexec/PlistBuddy -c "Print :persistent-apps:'${num}'" "${dockplistfile}" #done /usr/libexec/PlistBuddy -c save "${dockplistfile}" &>/dev/null done /usr/bin/killall -HUP Dock return 0 } app2dock_fp /Applications/Chess.app /Applications/Chess.app app2dock chess grapher remove_app_from_dock chess grapher