Skip to content

Instantly share code, notes, and snippets.

@felixbuenemann
Created November 15, 2020 20:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save felixbuenemann/e4df9485111dbaefd2f3ecabff5bcb20 to your computer and use it in GitHub Desktop.
Save felixbuenemann/e4df9485111dbaefd2f3ecabff5bcb20 to your computer and use it in GitHub Desktop.
#!/bin/sh
# pkg-remove removes macOS packages using the receipt
# Copyright: 2020 by Felix Buenemann
# License: MIT
set -eo pipefail
volume=/
dryrun=false
force=false
for arg in "$@"; do
case $arg in
--volume=*)
volume=${arg#*=}
shift
;;
-v|--volume)
shift
volume=$1
shift
;;
-n|--dry-run)
force=true
dryrun=true
shift
;;
-f|--force)
force=true
shift
;;
-h|--help)
echo "Usage: $0 [-n|--dry-run] [v|--volume VOLUME] PKGID [..PKGID]"
exit 0
;;
-*)
echo "Unknown option: $arg (see -h for help)" >/dev/stderr
exit 1
;;
esac
done
if $dryrun; then
echo "Dry-run active, simulating changes. Nothing will be removed!" >/dev/stderr
fi
for pkgid in "$@"; do
location=`pkgutil --volume "$volume" --pkg-info "$pkgid" | \
awk -F': ' '/^(volume|location):/{printf $2}' | tr -s /`
root=`pkgutil --volume "$volume" --only-dirs --files "$pkgid" | head -n1 ||:`
echo $root
if [ ! -d "$location" ]; then
echo "Package $pkgid install location '$location' doesn't exist" >/dev/stderr
else
if ! $force; then
read -p "Remove package $pkgid from '$location'? (y/N) " remove
if [ "$remove" != "y" ]; then
echo "Skipping $pkgid" >/dev/stderr
continue
fi
else
echo "Removing package $pkgid from '$location'" >/dev/stderr
fi
extra=
if [ -d "$location/$root/Contents/_MASReceipt" ]; then
echo "Detected Mac AppStore receipt, adding to deleted files" >/dev/stderr
extra="$root/Contents/_MASReceipt\n$root/Contents/_MASReceipt/receipt\n"
fi
if $dryrun; then
(pkgutil --volume "$volume" --files "$pkgid" && printf "$extra") \
| tail -r \
| awk "{ printf \"$location/%s%c\", \$0, 0 } END { printf \"$location\" }" \
| tr -s / \
| xargs -0 -n1 echo rm -df
else
# use relative paths so we can do more deletes in a single rm call
(pkgutil --volume "$volume" --files "$pkgid" && printf "$extra") \
| tail -r \
| tr '\n' '\0' \
| (cd "$location" && sudo xargs -0 rm -df)
# ignore errors when removing location (eg. /Applications)
sudo rm -df "$location" 2>/dev/null ||:
fi
fi
if ! $dryrun && [ "$root" != "" ] && [ -d "$location/$root" ]; then
echo "Package root directory '$location/$root' still exists!" >/dev/stderr
continue
fi
if ! $force; then
read -p "Remove package $pkgid install receipt? (y/N) " remove
if [ "$remove" != "y" ]; then
echo "Skipping $pkgid" >/dev/stderr
continue
fi
else
echo "Removing package $pkgid install receipt" >/dev/stderr
fi
if ! $dryrun; then
sudo pkgutil --volume "$volume" --forget "$pkgid"
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment