Bash – Cleanup Script for macOS

bashosxshell-script

I have a bunch of shared Mac workstations with dozens of users. These users constantly leave large files on their desktop and in the trash, which eventually fills up the local hard drive. I want to build a basic shell script that will delete desktop files and folders older than a set number of days, and empty the trash for all users. This is what I have come up with so far:

# Delete desktop files and folders older than 30 days
sudo find /Users/*/Desktop/ -type d -or -type f ! -name '.DS_Store' ! 
-name '.localized' -mtime +30 -exec rm -rf '{}' +;

# Empty Trash for all users
rm -rf /Users/*/.Trash/*

I understand the potential danger of getting the syntax wrong on a script like this. I have tested running this on just my account (replaced * with my user), and while it seems to work for files, the old folders on my desktop are not getting deleted. I'm wondering what I'm getting wrong, if there is perhaps a better way to execute this, and maybe add some polish with a confirmation dialog (ie. "WARNING: This will permanently delete all desktop files and folders older than 30 days, and empty the trash for all users! OK to proceed?").

Best Answer

Automatic Trash Removal

To set the thirty-day automatic Trash removal option for $username:

sudo -u $username bash -c 'defaults write com.apple.finder FXRemoveOldTrashItems -bool true'

Presenting a Dialog

Please note that to get the dialog portion working on a SIP-enabled Mojave, each user requires an entry in System Preferences > Security & Privacy > Privacy > Automation to give a terminal access to System Events.app, which displays the dialog. Here's one using osascript that looks as follows.

macOS or OS X dialog from shell script

If a user clicks "Yes," then the exit code from the dialog is 0 (zero). One might incorporate knowledge of the dialog's successful exit code with a script, as shown below.

#!/usr/bin/env bash
osascript -e 'tell app "System Events" to display dialog "Hi. I am automated script. May I delete Desktop & Trash files older than 30 days?" buttons {"Yes", "No"} with icon caution' >/dev/null 2>&1 

# $? is the exit code of the very last command that was executed (osascript).
#
if [ $? -eq 0 ]; then
    # Do something.
    echo "You clicked Yes."
fi

Finding Files

Below, finding files of specific types (file and directory) whose names are not .DS_Store or .localized and that are 30+ days old. The first line is for a safe test. Swap with the line below it to delete found objects. If an option needs to be specified more than once, use -o. Be sure to escape (\) special characters. The whitespace at the beginning and end of the parentheses is also important.

#!/usr/bin/env bash
find "$HOME/Desktop" -type f -o -type d -mtime +30 \! \( -name '.DS_Store' -o -name '.localized' \) -print
# find "$HOME/Desktop" -type f -o -type d -mtime +30 \! \( -name '.DS_Store' -o -name '.localized' \) -exec rm -rf '{}' +

Listing Users

#!/usr/bin/env bash
# Getting a list of users, filtering out service accounts, root, daemon, and nobody...
users=$(dscl . list /Users | grep -v -e '_' -e 'root' -e 'daemon' -e 'nobody')

for user in "$users"; do
    # Do something.
    id "$user"
done

unset users

Obviously there is some interpolation to do in order to stitch these snippets into a working script, but I hope it provides at least a bit more insight. Were it me, I think I might try to keep the script in /usr/local/bin and then set up a cron job to execute the script monthly.

Related Question