#!/usr/bin/env sh # POSIX shell implementation of Gitrect. # Clean up folders not present in the git-list # For all directories found in $WORKDIR # Operate on tempfile TEMPFILE="/tmp/gitrect.temp.$(od -t x1 -An -N6 /dev/urandom | tr -d '\n ')" touch "$TEMPFILE" PREFIX="/tmp/gitrect.prefix.$(od -t x1 -An -N6 /dev/urandom | tr -d '\n ')" touch "$PREFIX" # Reset all variables that might be set DEFAULT_STATE="$HOME/.local/share/git/repolist" WORKDIR="$HOME/.code" verbose=0 # Variables to be evaluated as shell arithmetic # should be initialized to a default or validated beforehand. usage="$(basename "$0") [-h] [-v -w WORKDIR -f FILE] Scan directories in the state file and delete those not referenced: -w set the working directory (default: ${WORKDIR}) -f set the git list file (default: ${DEFAULT_STATE}) -h show this help text -v increase output verbosity " while :; do case $1 in -h | -\? | --help) # Call a "show_help" function to display a synopsis, then exit. echo "$usage" exit ;; -f) # Takes an option argument, ensuring it has been specified. if [ -n "$2" ]; then DEFAULT_STATE="$2" shift fi ;; -f=?*) DEFAULT_STATE=${1#*=} # Delete everything up to "=" and assign the remainder. ;; -f=) # Handle the case of an empty --file= printf 'ERROR: "-f" requires a non-empty option argument.\n' >&2 exit 1 ;; -w) # Takes an option argument, ensuring it has been specified. if [ -n "$2" ]; then WORKDIR="$2" shift fi ;; -w=?*) WORKDIR=${1#*=} # Delete everything up to "=" and assign the remainder. ;; -w=) # Handle the case of an empty --file= printf 'ERROR: "-w" requires a non-empty option argument.\n' >&2 exit 1 ;; -v) verbose=$((verbose + 1)) # Each -v argument adds 1 to verbosity. ;; --) # End of all options. shift break ;; -?*) printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2 ;; *) # Default case: If no more options then break out of the loop. break ;; esac shift done # If there are input files (for example) that follow the options, they # will remain in the "$@" positional parameters. # No posix-compliant way of getting processor count, so leverage OS-specifics FS_SEPARATOR="/" if [ "$(uname -s)" = "Darwin" ]; then NPROC=$(eval "sysctl -n hw.ncpu") elif [ "$(uname -s)" = "Linux" ]; then NPROC=$(eval "nproc") else # Windows uses backslash FS_SEPARATOR="\\" if [ $verbose -gt 0 ]; then echo "cannot detect CPU total" fi fi # This double loop is super inefficient, but, it works (?) while read -r line; do dir=$(echo "$line" | cut -d, -f1 | rev | cut -d/ -f2- | rev) #top directories echo "${dir}"\$ >>"$PREFIX" done <"$DEFAULT_STATE" uniq "$PREFIX" "$TEMPFILE" rm "$PREFIX" while read -r line; do dir=$(echo "$line" | cut -d, -f 1) echo "$dir" >>"$TEMPFILE" done <"$DEFAULT_STATE" if [ $verbose -gt 0 ]; then find "${WORKDIR}"/* -depth | sed -e "s,${WORKDIR}/,," | grep -E -v -f "${TEMPFILE}" | xargs -P "$NPROC" -t -r -I {} rm -rf "${WORKDIR}""${FS_SEPARATOR}"{} else find "${WORKDIR}"/* -depth | sed -e "s,${WORKDIR}/,," | grep -E -v -f "${TEMPFILE}" | xargs -P "$NPROC" -r -I {} rm -rf "${WORKDIR}""${FS_SEPARATOR}"{} fi rm "$TEMPFILE"