Skip to main content

To aid end-users in determining where all their hard drive space has gone, we have a Self Service policy which leverages du and outputs text files of the top 75 directories of the root volume and the user's home folder. The utilitarian results are sorted by size, in gigabytes, and saved to the user's desktop.

Updated (28-May-2022)

Please see this post for a macOS Monterey-compatible version.

Self Service Description

Disk Usage Report Outputs two text files to your Desktop, which includes a listing of the top 75 directories of the root volume and the top 75 directories of your user folder. The results are saved to your desktop as: Computer-DiskUsage.txt and {YourUserName}-DiskUsage.txt.

Disk Usage: Root Volume

#!/bin/sh
####################################################################################################
#
# ABOUT
#
#   Disk Usage
#
####################################################################################################
#
# HISTORY
#
#   Version 1.0, 8-Dec-2014, Dan K. Snelson
#
####################################################################################################
# Import logging functions
source /path/to/logging/script/logging.sh
####################################################################################################

loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
loggedInUserHome=`dscl . -read /Users/$loggedInUser | grep NFSHomeDirectory: | cut -c 19- | head -n 1`
machineName=`scutil --get LocalHostName`

/bin/echo "`now` *** Calculate Disk Usage for / ***" >> $logFile

/usr/bin/du -axrg / | sort -nr | head -n 75 > $loggedInUserHome/Desktop/$machineName-ComputerDiskUsage.txt


exit 0      ## Success
exit 1      ## Failure

Disk Usage: Home Directory

#!/bin/sh
####################################################################################################
#
# ABOUT
#
#   Disk Usage
#
####################################################################################################
#
# HISTORY
#
#   Version 1.0, 8-Dec-2014, Dan K. Snelson
#
####################################################################################################
# Import logging functions
source /path/to/logging/script/logging.sh
####################################################################################################

loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
loggedInUserHome=`dscl . -read /Users/$loggedInUser | grep NFSHomeDirectory: | cut -c 19- | head -n 1`

/bin/echo "`now` *** Calculate Disk Usage for $loggedInUserHome  ***" >> $logFile

/usr/bin/du -axrg "$loggedInUserHome" | sort -nr | head -n 75 > "$loggedInUserHome"/Desktop/"$loggedInUser"-DiskUsage.txt


exit 0      ## Success
exit 1      ## Failure

 

@rgranholm Comment out (#) my lame logging functions:

# source /var/scripts/logging.sh

… and …

# bin/echo "`now` *** Calculate Disk Usage for / ***" >> $logFile

@rgranholm You have to copy the script located at this post above into a place like /var/scripts/ or whatever location makes sense for you. If you put a blank file there its not going to be able to load the script for logging.

Once that's there, just update my script above where it says:

source /var/scripts/logging.sh

to make sure the location and name of the script you saved from the step above match. Dan is using one script called something like "logging.sh" to do the log functions, which is loaded within the second script that does the disk usage information stuff by calling it with the source line. Does that make sense?


It does, and it worked, the log was sparse online after running, but it produced a log file in the location.

2016-04-14 15:58:35 Calculate Disk Usage for /

Thanks


Posting an updated version that includes uploading the final log as an attachment to the JSS Computer record. It determines the JSS ID of the machine its running on and uses that to upload the file.

You need to use an API account that has read and write privileges to computer objects, and perhaps a few other things. Not 100% sure of the required privs since I used my all purpose API account for testing, which has access to everything.

Obviously you can just remove the whole upload attachment section, or the send email section and use whichever one you want, or keep both. When I ran a test, I got an email with the attachment AND saw the new attachment in my computer record in our JSS that I could download and view.

#!/bin/sh
####################################################################################################
#
# ABOUT
#
#   Disk Usage
#
####################################################################################################
#
# HISTORY
#
#   Version 1.0, 8-Dec-2014, Dan K. Snelson
#
####################################################################################################
# Import logging functions
source /var/scripts/logging.sh
####################################################################################################

loggedInUser=$(stat -f%Su /dev/console)
loggedInUserHome=$(dscl . read /Users/$loggedInUser NFSHomeDirectory | awk '{print $NF}')
machineName=$(scutil --get LocalHostName)
dateString=$(date +"%Y-%m-%d")            ## Added date string for use with the local output filename

/bin/echo "`now` *** Calculate Disk Usage for / ***" >> $logFile

/usr/bin/du -axrg / | sort -nr | head -n 75 > $loggedInUserHome/Desktop/$machineName-ComputerDiskUsage-${dateString}.txt       ## Changed file name to use date string at end

############################# Section for emailing log as attachment ###############################

## var of path to log file
userLogFile="$loggedInUserHome/Desktop/$machineName-ComputerDiskUsage-${dateString}.txt "      ## Changed file reference to reflect the date in the file name being generated
logFileName=$(basename "$userLogFile")

## Email recipient address (customize this address!)
emailRec="somebody@mycompany.com"

(echo "Disk usage report attached for ${machineName} - $(date)"; uuencode "$userLogFile" "$logFileName") | mail -s "Disk Usage Report - ${machineName}" "$emailRec"


##################### Section for uploading log as attachment using the API ########################

## API Username & Password (account needs write privs to Computer objects)
## You can also pass the API Username and Password as script parameters in the policy ($4 and $5) instead of hardcoding them in (more secure)
apiuser="apiuser"
apipass="apipass"

## Get the JSS URL from the Mac the script is running on
jssURL=$(defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url | sed 's|/$||')

## Pull ioreg data on the Mac
ioregData=$(ioreg -rd1 -c IOPlatformExpertDevice)

## Get the Mac's Serial Number and UUID
SerialNum=$(awk -F'"' '/IOPlatformSerialNumber/{print $4}' <<< "$ioregData")
UUID=$(awk -F'"' '/IOPlatformUUID/{print $4}' <<< "$ioregData")

## Determine which one to use to pull the Mac's JSS ID
if [[ -z "$SerialNum" ]] && [[ ! -z "$UUID" ]]; then
    sourceID="$UUID"
    sourceString="uuid"
elif [[ ! -z "$SerialNum" ]]; then
    sourceID="$SerialNum"
    sourceString="serialnumber"
fi

## Get the Mac's JSS ID via API call
jssID=$(curl -H "Accept: text/xml" -sfku "${apiuser}:${apipass}" "${jssURL}/JSSResource/computers/${sourceString}/${sourceID}/subset/general" | xmllint --format - | awk -F'>|<' '/<id>/{print $3; exit}')

## Upload attachment via API, using the JSS Computer ID
curl -fku "${apiuser}:${apipass}" "${jssURL}/JSSResource/fileuploads/computers/id/${jssID}" -F name=@"${userLogFile}" -X POST

EDIT: Made a slight change to the local output file name to include a date string. Reason: If you run this multiple times per machine, before all uploading files would have an identical name, with no way of easily knowing which was the most recent. The date string being part of the file name now differentiates them.


Awesome, time to test it out! Thanks so much for all this help, it's going to be incredibly useful for reporting users who are abusing the space on their computers for personal content.

I'm completely new to the API, if I'm a JSS admin, can I assume my username/password will work for the API username and password?

Where should I start to dig if the e-mail continues to fail?

Checking for policies triggered by "recurring check-in" for user "rgranholm"...
Executing Policy TEST RFG - Disk Usage Report
Running script disk_usage_report.sh...
Script exit code: 26
Script result: 
Error running script: return code was 26.
Submitting log to https://jss.insigniam.com:8443/

Also, if you are bound to AD and maintain the email address field in AD, you can use this to capture that address to send the report:

emailRec=$(dscl . read /Users/$loggedInUser EMailAddress | awk '{print $NF}')

Thanks, for our purposes, sending it one static department address will work, but I have no idea how to get the e-mail to actually send, does anyone know what the error code 26 refers to?


@rgranholm Wish I could help, but not really sure where the issue would be. I don't know what error 26 means. I'm not even sure its related to sending the mail, but I assume it is.

Have you double checked everything in the script you're working with to make sure its correct? If I use the one I posted above and simply change a few variables to make sure they are valid, it works perfectly for me.

I did have one other thought for you. Can you try just manually sending yourself an email from the command line? Something like this?

echo "This is the message" | mail -s "This is the subject" youremailaddress@company.com

See if that goes thru OK and you receive it. If it does, then it may be something around the attachment causing the error. If that errors too, then its just sending mail in general that would be the issue.


Thanks, yeah no e-mail with that either, but no error. Thanks for the help this far, looks like I'll have to dig into some materials and work through it, part of the job eh! Thank you.


FWIW, we use launchd to handle this and set a threshold value, e.g. 85%. We have a separate Users drive, but it could be adapted.

To get percentage used:

df /Users |  awk  '/dev/disk/  {print substr($5,1,length($5) - 1)}'

As a LaunchAgent you can just use $HOME, so you don't have to worry about who is logged in or how to obtain
their home path.

/usr/bin/du -axrg "$HOME" | sort -nr | head -n 75

With this method, you aren't waiting for the user to completely fill their disk and then work out why it is full. Instead, with the periodic nature of launchd, it can help prompt the user to make sure they don't let it get too full in the first place.

Regarding email, this may be your mail server. No error is just saying the command ran correctly, but it will have no idea if the email server received the mail or did anything with it. Did you check your junk folder. Mails like this can easily become junked.


Happy New Year!

I just now noticed the verbiage has changed in macOS 10.12 (i.e., "Volume Free Space:" vs. "Volume Available Space:").

Here's an updated version, sans some of the racing stripes from above.

#!/bin/sh
####################################################################################################
#
# ABOUT
#
#   Disk Usage: Home Directory
#
####################################################################################################
#
# HISTORY
#
#   Version 1.0, 8-Dec-2014, Dan K. Snelson
#   Version 1.1, 8-Jun-2015, Dan K. Snelson
#       See: https://jamfnation.jamfsoftware.com/discussion.html?id=14701
#   Version 1.2, 4-Jan-2017, Dan K. Snelson
#       Updated for macOS 10.12
#
####################################################################################################
# Import logging functions
source /path/to/client-side/logging/script/logging.sh
####################################################################################################


# Variables
loggedInUser=$(/usr/bin/stat -f%Su /dev/console)
loggedInUserHome=$(/usr/bin/dscl . -read /Users/$loggedInUser NFSHomeDirectory | /usr/bin/awk '{print $NF}') # mm2270
machineName=$(/usr/sbin/scutil --get LocalHostName)
volumeName=$(/usr/sbin/diskutil info / | grep "Volume Name:" | awk '{print $3,$4}')

osMinorVersion=$(/usr/bin/sw_vers -productVersion | /usr/bin/cut -d. -f2)

    if [[ "$osMinorVersion" -lt 12 ]]; then
        availableSpace=$(/usr/sbin/diskutil info / | grep "Volume Free Space:" | awk '{print $4}')
        totalSpace=$(/usr/sbin/diskutil info / | grep "Total Size:" | awk '{print $3}')
    else
        availableSpace=$(/usr/sbin/diskutil info / | grep "Volume Available Space:" | awk '{print $4}')
        totalSpace=$(/usr/sbin/diskutil info / | grep "Volume Total Space:" | awk '{print $4}')
    fi

percentageAvailable=$(/bin/echo "scale=1; ($availableSpace / $totalSpace) * 100" | bc)
outputFileName="$loggedInUserHome/Desktop/$loggedInUser-DiskUsage.txt"


# Output to log
ScriptLog "### Disk usage for "$loggedInUserHome" ###"
ScriptLog "* Available Space:  $availableSpace GB"
ScriptLog "* Total Space:      $totalSpace GB"
ScriptLog "* Percentage Free:  $percentageAvailable%"


# Output to user
/bin/echo "--------------------------------------------------" > $outputFileName
/bin/echo "`now` Disk usage for "$loggedInUserHome"" >> $outputFileName
/bin/echo "* Available Space:  $availableSpace GB" >> $outputFileName
/bin/echo "* Total Space:      $totalSpace GB" >> $outputFileName
/bin/echo "* Percentage Free:  $percentageAvailable%" >> $outputFileName
/bin/echo "--------------------------------------------------" >> $outputFileName
/bin/echo " " >> $outputFileName
/bin/echo "GBs Directory or File" >> $outputFileName
/bin/echo " " >> $outputFileName
#/usr/bin/du -axrg / | /usr/bin/sort -nr | /usr/bin/head -n 75 >> $outputFileName
/usr/bin/du -axrg $loggedInUserHome | /usr/bin/sort -nr | /usr/bin/head -n 75 >> $outputFileName


if [ -f $outputFileName ]; then
    /usr/bin/su - $loggedInUser -c "open -a safari $outputFileName" # thanks, Samuel Look
fi

exit 0      ## Success
exit 1      ## Failure

Happy New Year!

I just now noticed the verbiage has changed in macOS 10.12 (i.e., "Volume Free Space:" vs. "Volume Available Space:").

Here's an updated version, sans some of the racing stripes from above.

#!/bin/sh
####################################################################################################
#
# ABOUT
#
#   Disk Usage: Home Directory
#
####################################################################################################
#
# HISTORY
#
#   Version 1.0, 8-Dec-2014, Dan K. Snelson
#   Version 1.1, 8-Jun-2015, Dan K. Snelson
#       See: https://jamfnation.jamfsoftware.com/discussion.html?id=14701
#   Version 1.2, 4-Jan-2017, Dan K. Snelson
#       Updated for macOS 10.12
#
####################################################################################################
# Import logging functions
source /path/to/client-side/logging/script/logging.sh
####################################################################################################


# Variables
loggedInUser=$(/usr/bin/stat -f%Su /dev/console)
loggedInUserHome=$(/usr/bin/dscl . -read /Users/$loggedInUser NFSHomeDirectory | /usr/bin/awk '{print $NF}') # mm2270
machineName=$(/usr/sbin/scutil --get LocalHostName)
volumeName=$(/usr/sbin/diskutil info / | grep "Volume Name:" | awk '{print $3,$4}')

osMinorVersion=$(/usr/bin/sw_vers -productVersion | /usr/bin/cut -d. -f2)

    if [[ "$osMinorVersion" -lt 12 ]]; then
        availableSpace=$(/usr/sbin/diskutil info / | grep "Volume Free Space:" | awk '{print $4}')
        totalSpace=$(/usr/sbin/diskutil info / | grep "Total Size:" | awk '{print $3}')
    else
        availableSpace=$(/usr/sbin/diskutil info / | grep "Volume Available Space:" | awk '{print $4}')
        totalSpace=$(/usr/sbin/diskutil info / | grep "Volume Total Space:" | awk '{print $4}')
    fi

percentageAvailable=$(/bin/echo "scale=1; ($availableSpace / $totalSpace) * 100" | bc)
outputFileName="$loggedInUserHome/Desktop/$loggedInUser-DiskUsage.txt"


# Output to log
ScriptLog "### Disk usage for "$loggedInUserHome" ###"
ScriptLog "* Available Space:  $availableSpace GB"
ScriptLog "* Total Space:      $totalSpace GB"
ScriptLog "* Percentage Free:  $percentageAvailable%"


# Output to user
/bin/echo "--------------------------------------------------" > $outputFileName
/bin/echo "`now` Disk usage for "$loggedInUserHome"" >> $outputFileName
/bin/echo "* Available Space:  $availableSpace GB" >> $outputFileName
/bin/echo "* Total Space:      $totalSpace GB" >> $outputFileName
/bin/echo "* Percentage Free:  $percentageAvailable%" >> $outputFileName
/bin/echo "--------------------------------------------------" >> $outputFileName
/bin/echo " " >> $outputFileName
/bin/echo "GBs Directory or File" >> $outputFileName
/bin/echo " " >> $outputFileName
#/usr/bin/du -axrg / | /usr/bin/sort -nr | /usr/bin/head -n 75 >> $outputFileName
/usr/bin/du -axrg $loggedInUserHome | /usr/bin/sort -nr | /usr/bin/head -n 75 >> $outputFileName


if [ -f $outputFileName ]; then
    /usr/bin/su - $loggedInUser -c "open -a safari $outputFileName" # thanks, Samuel Look
fi

exit 0      ## Success
exit 1      ## Failure

Is there any update for this code for Monterey? Been running into issues running it :(. 

 

#!/bin/sh #################################################################################################### # # ABOUT # # Disk Usage: Home Directory # #################################################################################################### # # HISTORY # # Version 1.0, 8-Dec-2014, Dan K. Snelson # Version 1.1, 8-Jun-2015, Dan K. Snelson # See: https://jamfnation.jamfsoftware.com/discussion.html?id=14701 # Version 1.2, 4-Jan-2017, Dan K. Snelson # Updated for macOS 10.12 # #################################################################################################### # Import logging functions # source /var/scripts/logging.sh #################################################################################################### # Variables loggedInUser=$(/usr/bin/stat -f%Su /dev/console) loggedInUserHome=$(/usr/bin/dscl . -read /Users/$loggedInUser NFSHomeDirectory | /usr/bin/awk '{print $NF}') # mm2270 machineName=$(/usr/sbin/scutil --get LocalHostName) volumeName=$(/usr/sbin/diskutil info / | grep "Volume Name:" | awk '{print $3,$4}') availableSpace=$(/usr/sbin/diskutil info / | grep "Container Free Space:" | awk '{print $4}') totalSpace=$(/usr/sbin/diskutil info / | grep "Container Total Space:" | awk '{print $3}') percentageAvailable=$(/bin/echo "scale=1; ($availableSpace / $totalSpace) * 100" | bc) outputFileName="$loggedInUserHome/Desktop/$loggedInUser-DiskUsage.txt" # Output to log ScriptLog "### Disk usage for "$loggedInUserHome" ###" ScriptLog "* Available Space: $availableSpace GB" ScriptLog "* Total Space: $totalSpace GB" ScriptLog "* Percentage Free: $percentageAvailable%" # Output to user /bin/echo "--------------------------------------------------" > $outputFileName /bin/echo "`now` Disk usage for "$loggedInUserHome"" >> $outputFileName /bin/echo "* Available Space: $availableSpace GB" >> $outputFileName /bin/echo "* Total Space: $totalSpace GB" >> $outputFileName /bin/echo "* Percentage Free: $percentageAvailable%" >> $outputFileName /bin/echo "--------------------------------------------------" >> $outputFileName /bin/echo " " >> $outputFileName /bin/echo "GBs Directory or File" >> $outputFileName /bin/echo " " >> $outputFileName #/usr/bin/du -axrg / | /usr/bin/sort -nr | /usr/bin/head -n 75 >> $outputFileName /usr/bin/du -axrg $loggedInUserHome | /usr/bin/sort -nr | /usr/bin/head -n 75 >> $outputFileName if [ -f $outputFileName ]; then /usr/bin/su - $loggedInUser -c "open -a safari $outputFileName" # thanks, Samuel Look fi exit 0 ## Success exit 1 ## Failure

 

I'm currently using this but when I get the output file the  "Total Space" and "Percentage Free" is blank :( 


@MPL The following update may prove helpful:

https://snelson.us/2022/05/disk-usage-report-monterey-compatible/