├── .gitignore ├── LICENSE ├── README.md ├── _zap └── zap /.gitignore: -------------------------------------------------------------------------------- 1 | ####### OSX ####### 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must end with two \r 7 | Icon 8 | 9 | # Thumbnails 10 | ._* 11 | 12 | # Files that might appear on external disk 13 | .Spotlight-V100 14 | .Trashes 15 | 16 | # Directories potentially created on remote AFP share 17 | .AppleDB 18 | .AppleDesktop 19 | Network Trash Folder 20 | Temporary Items 21 | .apdisk 22 | 23 | 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Keith Smiley (http://keith.so) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the 'Software'), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zap 2 | 3 | A simple command line tool to remove a `.app` on macOS and its [related 4 | files](https://github.com/keith/zap/blob/master/zap#L80-90). 5 | 6 | Think [AppZapper](http://www.appzapper.com) from the command line. 7 | 8 | **Zap asks you for permission before deleting each file** 9 | 10 | ## Usage 11 | 12 | To delete an application in `/Applications` or `~/Applications` 13 | 14 | ```sh 15 | zap foo.app 16 | ``` 17 | 18 | To delete a specific application: 19 | 20 | ```sh 21 | zap /path/foo.app 22 | ``` 23 | 24 | To delete an application securely use `-s` (this must come before the 25 | application): 26 | 27 | ```sh 28 | zap -s foo.app 29 | ``` 30 | 31 | ### Installation 32 | 33 | With [Homebrew](http://brew.sh) 34 | 35 | ```sh 36 | brew install keith/formulae/zap 37 | ``` 38 | 39 | Without Homebrew just copy `zap` to somewhere in your `$PATH`. If you 40 | would also like the zsh completions copy `_zap` to somewhere in your 41 | `$fpath`. 42 | 43 | #### Disclaimer 44 | 45 | This CLI deletes stuff. If it deletes the wrong stuff it isn't my fault. 46 | -------------------------------------------------------------------------------- /_zap: -------------------------------------------------------------------------------- 1 | #compdef zap 2 | 3 | _files -W "/Applications" -g "*.app" 4 | _files -W "$HOME/Applications" -g "*.app" 5 | -------------------------------------------------------------------------------- /zap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | function usage() 6 | { 7 | echo "Usage: zap [-y] [appname]" 8 | exit 1 9 | } 10 | 11 | auto_confirm=0 12 | 13 | function remove() 14 | { 15 | paths=("$@") 16 | for path in "${paths[@]}" 17 | do 18 | if [[ -e $path ]]; then 19 | if [[ "$auto_confirm" == 1 ]]; then 20 | echo "Removing $path" 21 | rm -r "$path" 22 | return 23 | fi 24 | 25 | read -p "Remove $path? " -r 26 | if [[ $REPLY =~ ^[Yy]$ ]]; then 27 | rm -r "$path" 28 | elif [[ ! $REPLY =~ ^[Nn]$ ]]; then 29 | remove "$path" 30 | fi 31 | fi 32 | done 33 | } 34 | 35 | if [[ $# -lt 1 ]]; then 36 | usage 37 | fi 38 | 39 | plist="/usr/libexec/PlistBuddy" 40 | info_plist="/Contents/Info.plist" 41 | if [[ $1 == "-y" ]]; then 42 | auto_confirm=1 43 | shift 44 | elif [[ $# -gt 1 ]]; then 45 | usage 46 | fi 47 | 48 | app="$1" 49 | app_path="$app" 50 | if [[ ! -d $app_path ]]; then 51 | app_path="/Applications/${app%.app}.app" 52 | 53 | if [[ ! -d $app_path ]]; then 54 | app_path="$HOME/Applications/${app%.app}.app" 55 | fi 56 | 57 | if [[ ! -d $app_path ]]; then 58 | echo "Application path must be absolute or in /Applications or $HOME/Applications" 59 | exit 1 60 | fi 61 | fi 62 | 63 | if (( EUID != 0 )); then 64 | if [[ ! -w $app_path ]]; then 65 | echo "$app_path cannot be deleted. Try running this again with 'sudo'" 66 | exit 1 67 | fi 68 | fi 69 | 70 | plist_path="${app_path%/}$info_plist" 71 | if [[ ! -f $plist_path ]]; then 72 | echo "No plist at $plist_path" 73 | exit 1 74 | fi 75 | 76 | identifier=$($plist -c 'print :CFBundleIdentifier' "$plist_path") 77 | if [[ -z "$identifier" ]]; then 78 | echo "Couldn't determine bundle identifier '$identifier'" 79 | exit 1 80 | fi 81 | 82 | appname=$(basename "${app_path%.*}") 83 | pkill -f "$app_path" || true 84 | lines=$(pgrep -f "$(echo "$app_path" | sed -E 's/(.)/[\1]/')" | wc -l | xargs || true) 85 | if [[ $lines -gt 0 ]]; then 86 | echo "Please quit $appname and try again" 87 | exit 1 88 | fi 89 | 90 | remove "$app_path" 91 | remove "$HOME/Library/Application Support/$appname" 92 | remove "$HOME/Library/Application Support/$identifier" 93 | remove "$HOME/Library/Containers/$identifier"* 94 | remove "$HOME/Library/Caches/$appname" 95 | remove "$HOME/Library/Caches/$identifier" 96 | remove "$HOME/Library/$appname" 97 | remove "$HOME/Library/Preferences/"*"$identifier"*".plist" 98 | remove "$HOME/Library/Saved Application State/$identifier.savedState" 99 | remove "$HOME/Library/SyncedPreferences/$identifier"*".plist" 100 | remove "$HOME/Library/WebKit/$identifier" 101 | --------------------------------------------------------------------------------