Humbly requesting a User/Home Folder deletion script.

TechSpecialist
Contributor

Hello all,

Would anyone out there be able to provide me with a Script that on execution would delete all Users and/or User's home folder's with the exception of the user that is logged in and an option to specify specific (admin) users that need to stay on there?

It is likely that my Macs have a User Home folders that don't have a local user. So if the Local user doesn't exist, it still should wipe the Home folders of anyone but the current logged in user (and the specified admin user(s).

I would be tremendously gratefull!

1 ACCEPTED SOLUTION

dsavageED
Contributor III

Relatively easy to fix, the previous was taken from code I had...

Users_Array=( `ls /Users | grep -v Shared | grep -v UserNotToDelete1 | grep -v UserNotToDelete2` )
for user in "${Users_Array[@]}"
do
    if [ "${user}" == "${User_Name}" ]; then
        echo "skip user ${user} as they are logged on."
    else
        dscl . -delete /Users/${user}
        rm -fR /Users/${user}
    fi
done

View solution in original post

11 REPLIES 11

dsavageED
Contributor III

Sounds like you want something like:

#!/bin/sh
User_Name=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`

Users_Array=( `ls /Users | grep -v Shared` )
for user in "${Users_Array[@]}"
do
    User_Local=`dscl . -list /Users | grep "^${user}"`
    if [ "${user}" == "${User_Local}" ] || [ "${user}" == "${User_Name}" ]; then
        echo "skip user ${user} as they are local or logged on."
    else
        rm -fR /Users/${user}
    fi
done

exit 0;

TechSpecialist
Contributor

Hi there,

Thank you so much for your quick reply. I've tried your script, but It's not quite what I wanted.

"skip user ${user} as they are local or logged on."

What I mean is that I would need all local users to be deleted too (except for the user that is currently logged in). And in addition I would like to specify in the scrip itself a couple of user accounts that should always be skipped.

I do really appreciate the effort tho. Maybe someday I can return the favour for someone else!

dsavageED
Contributor III

Relatively easy to fix, the previous was taken from code I had...

Users_Array=( `ls /Users | grep -v Shared | grep -v UserNotToDelete1 | grep -v UserNotToDelete2` )
for user in "${Users_Array[@]}"
do
    if [ "${user}" == "${User_Name}" ]; then
        echo "skip user ${user} as they are logged on."
    else
        dscl . -delete /Users/${user}
        rm -fR /Users/${user}
    fi
done

thebrucecarter
Contributor II

Here is what we do (caveat, I had to sanitize this and pull in some stuff that we actually source from a library of our routines, so hopefully I didn't mess it up). We have a rolling delete so that if the machine crashes, or somebody forgets to save their stuff before they log out we don't have tears. You'd want to set homesToSave to 0 for your purpose. This is not necessarily a shining example of shell coding (I can say that since I wrote it). Also, we run this locally as part of a logouthook with the current user fed in as $1, so if you run it from Jamf you are probably going to need to change the way it gets the current user. It also somewhat depends on a reasonable local assumption that the home directory name is the same as the short user ID.

I don't know if this continues to work in Catalina, we're not taking the labs there until summer. It works fine for us in Mojave.

#!/bin/sh

#---------------------------------------------------------------------------------------------
#-- purgeoldhomes.sh
#---------------------------------------------------------------------------------------------

homesToSave=2
homesSaved=0
baseDirectory="/Users/"
CURRENTUSER="${1}"
ProtectedHomeFolders=( "${CURRENTUSER}" root ndadmin .localized Shared )

ProtectedHomeFolder()
{
    for folder in ${ProtectedHomeFolders[@]}
    do
        if [ "${1}" == "$folder" ] ; then 
            return 0
        fi
    done
    return 1
}

for homeFolder in `ls -t ${baseDirectory}`
do  
    # only process directories
    if [ -d ${baseDirectory}${homeFolder} ]; then
        #only process unprotected directories
        if  ! ProtectedHomeFolder "${homeFolder}" ; then
            #pardon the newest homes
            if [ $[homesSaved] -lt $[homesToSave] ] ; then
                ((homesSaved+=1))
            else
                # delete user
                /usr/bin/dscl . delete "${baseDirectory}${homeFolder}" > /dev/null 2>&1
                # delete home directory
                /usr/bin/chflags -Rf nouchg "${baseDirectory}${homeFolder}"
                /bin/rm -Rf "${baseDirectory}${homeFolder}"
            fi
        fi  
    fi  
done

exit 0

Hi and thank you for sharing.
I´m trying to protect the home folders of any admin home folders even they are not the last to sign in to the computer.
Is there any way to have the script skip admin users?

 

TechSpecialist
Contributor

@dsavageED

You are my Hero. This does exactly what I want it to do. I'm sure it might be nothing for you, but really you have made my day!

Thanks a lot!

bcrockett
Contributor III

@dsavageED

Your script is still working on macOS Catalina 10.15.5!

brent_prisbrey
New Contributor II

Now that JAMF has removed the logouthook, what are you doing to remove home folders?

You can run a script at login (not with loginhook, obviously, but by using the normal launch agent architecture) and then watch for when it gets killed (which should be logout).  Bing's AI actually came up with a pretty good answer for this, although I haven't tested its code:

## Bing
The logouthook feature has been deprecated for some time now and there is no obvious replacement for logout hooks in macOS. However, you can create a shell script that is launched on login and simply sleeps until a kill signal is received. Here's an example of how you can do this:

#!/bin/sh
onLogout () {
echo 'Logging out' >> ~/Logs/logout.sh.log
exit
}

trap 'onLogout' SIGINT SIGHUP SIGTERM

while true; do
sleep 86400 &
wait $!
done

This script will sleep until it receives one of the trapped signals, at which point it will execute the onLogout function. You can launch this script using a RunAtLoad launch agent or launch daemon and it will run at log-out or shutdown. Please note that tasks only have a limited amount of time to complete before they are killed, so this shouldn't be used to run anything that takes a long time, or requires a network connection that could be delayed.
For this to work, the shell scripts need to be executed directly by launchd, i.e., it shouldn't be invoked via sh. So if it were placed in ~/Library/Scripts/foo.sh your program arguments might look like:

<key>ProgramArguments</key>
<array>
<string>~/Library/Scripts/foo.sh</string>
<string>bar</string>
</array>
<key>EnableGlobbing</key>
<true/>

jwnola
New Contributor

I know this is old, but hopefully, someone can answer this. I can usually look at the script and figure out what I need to change even with my limited knowledge of the subject.

In this case, I can't figure out how to set the range of the users to keep based on when they last logged in. I can see the comment "#pardon the newest homes", but how do I change the timeframe? It seems like it's set to a month based on my testing. 

And this does seem to work on Mac OS X Ventura and Sonoma.

Thanks!

Greetings!  That particular version of our script works on a count of remaining home directories rather than an age, as that was what the department wanted.  So, we set a count (it is currently 2) and iterate on that.  To do it based on age, you'd need to replace the

 

if [ $[homesSaved] -lt $[homesToSave] ] ; then

 

logic with logic based on the age of either the home directory folder or a semaphore file.  I don't have code handy to illustrate that, but it should be fairly straightforward.  Same kind of logic, just using the age rather than the number of directories.