#!/bin/zsh Version="2.0" # ------ COLOR SETUP ------ # # Reset Color_Off='\033[0m' # Text Reset # Regular Colors Black='\033[0;30m' # Black Red='\033[0;31m' # Red Green='\033[0;32m' # Green Yellow='\033[0;33m' # Yellow Blue='\033[0;34m' # Blue Purple='\033[0;35m' # Purple Cyan='\033[0;36m' # Cyan White='\033[0;37m' # White # Bold BBlack='\033[1;30m' # Black BRed='\033[1;31m' # Red BGreen='\033[1;32m' # Green BYellow='\033[1;33m' # Yellow BBlue='\033[1;34m' # Blue BPurple='\033[1;35m' # Purple BCyan='\033[1;36m' # Cyan BWhite='\033[1;37m' # White ProgNameColor=$BYellow AuthorNameColor=$BGreen VersionColor=$BBlue BarColor=$Cyan FoundColor=$Blue SizeColor=$Green ClearableNameColor=$Yellow NotFoundColor=$Purple ResultSizeColor=$Green CategoryColor=$BYellow FoundText="${FoundColor}Found !${Color_Off}" # ------ CORE ------ # config_folder_filepath="${HOMEBREW_PREFIX}/etc/clearscript" config_filepath="${config_folder_filepath}/config.cfg" # Stops the script on any failure set -e # Array definitions typeset -A jobSteps typeset -A jobCategories # Associative arrays cannot contain other arrays, so this will contain strings with newlines as a workaround typeset -A jobEnabled # Upgrade the script permissions using sudo asksudo() { echo "Checking for sudo permissions..." if [[ "$EUID" = 0 ]]; then echo "${Green}Already have sudo !${Color_Off}" else sudo -k if sudo true; then echo "${Green}Correct password !${Color_Off}" else echo "${Red}Wrong password.${Color_Off}" exit 1 fi fi } # Add a job to cleanup something # Arguments: # - Category | The category of the job # - Name | The name of the job # - Step | Can be either a function or a folder path addJob() { local category="$1" local name="$2" local step="$3" jobSteps["$name"]="$step" if [[ -z $jobCategories["$category"] ]]; then jobCategories["$category"]="$name" else jobCategories["$category"]+="\n$name" fi } # Shows a title for a job element # Arguments: # - jobName | The job name jobTitle() { local jobName="$1" echo echo "${BarColor}[ ${ClearableNameColor}${jobName} ${BarColor}]${Color_Off}" } # Takes a size value and returns a human readable version. # Arguments: # - Return variable | The formatted variable # - valueInBytes | The size in bytes getHumanReadableValue() { local valueInBytes="$2" if [[ -z $valueInBytes ]]; then valueInBytes=0; fi local human_readable=0 if [[ $valueInBytes -ge 1073741824 ]]; then human_readable="$(($valueInBytes / 1073741824)) GB" elif [[ $valueInBytes -ge 1048576 ]]; then human_readable="$(($valueInBytes / 1048576)) MB" elif [[ $valueInBytes -ge 1024 ]]; then human_readable="$(($valueInBytes / 1024)) KB" else human_readable="${valueInBytes} bytes" fi local return_var="$1" eval "$return_var"='$human_readable' } # Updates the cleared value in bytes # Arguments: # - Return variable | Updated cleared value # - clearedValueToAdd | A number to add to the current cleared value. addClearedVal() { local clearedValueToAdd="$2" if [[ -z $clearedValueToAdd ]]; then clearedValueToAdd=0; fi if [[ -z $cleared ]]; then cleared=0; fi cleared=$(($cleared+$clearedValueToAdd)) local return_var="$1" eval "$return_var"='$cleared' } # Function used to clear every simple folder. # Arguments: # - clearableName | The name of the job. # - folderPath | The path of the folder to remove the content of. clearFolderNew() { local folderPath="$2" local clearableName="$1" jobTitle "$clearableName" echo "Checking for ${ClearableNameColor}${clearableName}${Color_Off}'s cache folder..." if [ -n "$(find "${folderPath}" -mindepth 1 -maxdepth 1 2>/dev/null)" ]; then echo "${FoundText}" sizeToClear="$(du -sk "${folderPath}" | cut -f1)" getHumanReadableValue readableSize $sizeToClear echo "Clearing ${ClearableNameColor}${clearableName}${Color_Off}'s cache... ${SizeColor}(${readableSize})${Color_Off}" addClearedVal cleared $sizeToClear sudo rm -rv "${folderPath}"/* | pv -l -s $(du -a "${folderPath}" | wc -l)> /dev/null else echo "${ClearableNameColor}${clearableName}${Color_Off}'s cache ${NotFoundColor}not found${Color_Off}." fi } # Runs every job runJobs() { # Elevate permissions... asksudo for category in ${(k)jobCategories}; do echo echo "${BarColor}[ --====-- $CategoryColor$category${BarColor} --====-- ]${Color_Off}" echo "$jobCategories[$category]" | while IFS= read -r jobName; do if [[ "${jobEnabled["\"$jobName\""]}" = true ]]; then local step=$jobSteps["$jobName"] if print -l ${(ok)functions} | grep -q "^$step$"; then jobTitle "$jobName" # Running the custom job function $step else clearFolderNew "$jobName" "$step" fi fi done done } read_config_file() { if [[ ! -f "$config_filepath" ]]; then if [[ ! -d "$config_folder_filepath" ]]; then mkdir -p $config_folder_filepath fi touch $config_filepath fi while IFS= read -r line; do parts=(${(s/=/)line}) configKey=${parts[1]} configValue=${parts[2]} # Hard code values to prevent a broken config file from destroying everything if [ "$configValue" = true ]; then jobEnabled["$configKey"]=true; else jobEnabled["$configKey"]=false; fi done < $config_filepath for job in ${(k)jobSteps}; do # If the job doesn't exist in the config file then it should be added in it if [[ -z "${jobEnabled["$job"]}" ]]; then echo "${job}=false" >> $config_filepath jobEnabled["$job"]=false; fi done } configure() { local doneEverything=false while [[ $doneEverything = false ]]; do typeset -A optionsToJob local all_options=() for job in ${(k)jobSteps}; do local current_option="" if [[ "${jobEnabled["$job"]}" = true ]]; then current_option="[ON] ${job}" else current_option="[OFF] ${job}" fi optionsToJob["$current_option"]="$job" all_options+=("$current_option") done all_options+=("Done") clear echo "=== CONFIGURATOR ===" echo "Choose what you want to be enabled: " select option in "${all_options[@]}"; do array_length=${#all_options[@]} if (( $REPLY < 0 || $REPLY > $array_length )); then echo "Invalid choice. Please select a valid option." break elif [[ $REPLY = $array_length ]]; then doneEverything=true break else jobToEdit=${optionsToJob["${all_options[$REPLY]}"]} local newValue="" if [[ "${jobEnabled["$jobToEdit"]}" = true ]]; then newValue=false else newValue=true fi # Modifying the value inside the config file sed -i -e "/^${jobToEdit}=/ s/=.*/=${newValue}/" "$config_filepath" break fi done read_config_file # Reread updated config file done } # ------ CUSTOM CLEANUP FUNCTIONS ------ # gradleJavaClear() { GradleJarFiles="${HOME}/.gradle/caches" echo "Checking for ${ClearableNameColor}gradle${Color_Off} java cache folders..." if [ -n "$(ls -d ${GradleJarFiles}/jars-* 2>/dev/null)" ]; then echo "${FoundText}" sizeToClear="$(du -sck ${GradleJarFiles}/jars-* | grep 'total' | cut -f1)" getHumanReadableValue readableSize $sizeToClear echo "Clearing ${ClearableNameColor}Gradle${Color_Off} java caches... ${SizeColor}($readableSize)${Color_Off}" addClearedVal cleared $sizeToClear sudo rm -rv ${GradleJarFiles}/jars-* | pv -l -s $(du -a ${GradleJarFiles}/jars-* | wc -l)> /dev/null else echo "${ClearableNameColor}Gradle${Color_Off} java cache folders ${NotFoundColor}not found${Color_Off}." fi } # Clean out older Fusion 360 installations deleteOldestInstallFusion360() { echo "Clearing outdated Fusion360 versions" local targetDirectory="${HOME}/Library/Application Support/Autodesk/webdeploy/production" if [[ -d "$targetDirectory" ]] then local linkToFind="$(readlink ${HOME}/Library/Application\ Support/Autodesk/webdeploy/production/Autodesk\ Fusion\ 360.app)" local extractedFolderName="$(basename "$(dirname "$linkToFind")")" echo "Target Directory is '""$targetDirectory""'" echo "Currently used Fusion360 folder: ${extractedFolderName}" if [ "$extractedFolderName" = "" ] || [ "$extractedFolderName" = "." ]; then echo "${NotFoundColor}Unable to find the right folder${Color_Off}" else if [ "$linkToFind" = "" ]; then echo "${NotFoundColor}Unable to find the right folder${Color_Off}" else local toDelete=0 # Calculating the size of files to delete with an overly complex command toDelete=$(($toDelete$(find "$targetDirectory" -mindepth 1 -maxdepth 1 ! -name "$extractedFolderName" ! -name "Autodesk Fusion 360.app" ! -name ".DS_Store" -exec bash -c 'echo -n "+$(du -sk "$0" | cut -f1)"' "{}" \; ))) getHumanReadableValue readableSize $toDelete echo "Deleting folders... ${SizeColor}(${readableSize})${Color_Off}" # Updating cleared addClearedVal cleared $toDelete # Clearing folders w/ progression find "$targetDirectory" -mindepth 1 -maxdepth 1 ! -name "$extractedFolderName" ! -name "Autodesk Fusion 360.app" ! -name ".DS_Store" -exec sh -c 'sudo rm -rv "$0" | pv -l -s $(du -a "$0" | wc -l)> /dev/null' "{}" \; echo "Done!" fi fi else echo "${ClearableNameColor}Fusion360${Color_Off}'s folder ${NotFoundColor}not found${Color_Off}." fi } clearDocker() { if docker_exists="$(type -p "docker")" || [[ -z $docker_exists ]]; then if [ "$(docker ps -a -q | wc -l)" -gt "0" ]; then echo "Found running docker containers, stopping them..." docker stop $(docker ps -a -q) docker rm $(docker ps -a -q) fi docker system prune --all --force else echo "${ClearableNameColor}Docker${Color_Off} was ${NotFoundColor}not found${Color_Off}." fi } # -- Steps definitions -- # addJob "Development" "JetBrains" "${HOME}/Library/Caches/JetBrains" addJob "Development" "CoreSimulator" "${HOME}/Library/Developer/CoreSimulator/Caches" addJob "Development" "VisualStudioInstaller" "${HOME}/Library/Caches/VisualStudioInstaller" addJob "Development" "Gradle java" gradleJavaClear addJob "Development" "VisualStudio" "${HOME}/Library/Caches/VisualStudio" addJob "Development" "Docker" clearDocker addJob "Browser" "Firefox" "${HOME}/Library/Caches/Firefox" addJob "Mail" "Thunderbird" "${HOME}/Library/Caches/Thunderbird" addJob "Miscellaneous" "Homebrew" "${HOME}/Library/Caches/Homebrew" addJob "Miscellaneous" "Discord" "${HOME}/Library/Application Support/discord/Cache" addJob "Miscellaneous" "Adobe" "${HOME}/Library/Caches/Adobe" addJob "Miscellaneous" "Fusion360 Old Installations" deleteOldestInstallFusion360 # -- Main Script -- # echo "${BarColor}==========================================${Color_Off}" echo " Script ${ProgNameColor}Clear.sh${Color_Off} by ${AuthorNameColor}Skydust " echo " ${VersionColor}v${Version}${Color_Off} " echo "${BarColor}==========================================${Color_Off}" read_config_file while true; do local response print -n "Want to configure? (y/N): " read response # Check the response case "$response" in [yY]) configure ;; *) break ;; esac done runJobs getHumanReadableValue readableSize $cleared # Print final echo "${BarColor}==========================================${Color_Off}" echo "Cleared ${ResultSizeColor}${readableSize}${Color_Off} !" echo "${BarColor}==========================================${Color_Off}"