#!/usr/bin/env bash # Cleanup set -e trap 'exit 1' SIGINT # Constants VERSION='2.0.0' RED=$'\e[1;31m' GREEN=$'\e[1;32m' BLUE=$'\e[1;34m' YELLOW=$'\e[1;33m' WHITE_BOLD=$'\e[1;37m' RESET=$'\e[0;0m' fd_opts=(--hidden --threads "$(nproc)") rm_opts=(--verbose) declare -a typeopts # Helper functions function help() { cat << HELPMESSAGE $(basename "$0") $VERSION Usage: $(basename "$0") [-h] [-d] [-e] [-E ext] [-f] [-F] [-I] [-i] [-l] patterns [patterns ...] positional arguments: patterns file matching patterns options: -h, --help show this help message and exit -d, --directories-only filter results to directories -e, --empty-only filter results to empty files and directories -E ext, --extension ext file extension -f, --files-only filter results to files -F, --force-directory-delete do not ignore non-empty directories, delete anyways -I, --no-ignore-vcs do not ignore .gitignore -i, --no-ignore do not ignore .gitignore and .fdignore -l, --links-only filter results to symlinks HELPMESSAGE } # $1 is the output string, $2 is the color function color_output() { case "$2" in 'red') printf "$RED%s\n" "$1" ;; 'green') printf "$GREEN%s\n" "$1" ;; 'blue') printf "$BLUE%s\n" "$1" ;; 'yellow') printf "$YELLOW%s\n" "$1" ;; 'white') printf "$WHITE_BOLD%s\n" "$1" ;; 'reset') printf "$RESET%s\n" "$1" ;; esac } # Color files blue and directories green function color_path() { if [[ -f "$1" ]]; then color_output "$1" 'blue' elif [[ -d "$1" ]]; then color_output "$1" 'green' fi } # Delete path using correct command # Parameters: # - $1: path # - $2: force delete boolean flag function delete() { exit 1 } while true; do case "${1}" in '-d' | '--directories-only') typeopts+=(--type directory) shift continue ;; '-e' | '--empty-only') typeopts+=(--type empty) shift continue ;; '-E' | '--extension') EXT="${2}" case "${EXT}" in "") exit 1 ;; -*) exit 1 ;; esac fd_opts+=(--extension "$EXT") shift 2 continue ;; --extension=*) EXT="${1#*=}" case "${EXT}" in "") exit 1 ;; -*) exit 1 ;; esac fd_opts+=(--extension "$EXT") shift continue ;; '-f' | '--files-only') typeopts+=(--type file) shift continue ;; '-F' | '--force-directory-delete') rm_opts+=(--recursive --force) shift continue ;; '-i' | '--no-ignore-vcs') fd_opts+=('--no-ignore-vcs') shift continue ;; '-I' | '--no-ignore') fd_opts+=('--no-ignore') shift continue ;; '-l' | '--links-only') typeopts+=(--type symlink) shift continue ;; '-h' | '--help') help exit ;; --) shift break ;; -*) printf '%s\n' "Unknown option: ${1}" >&2 exit 1 ;; *) break ;; esac done # Interpret options # If nothing was entered if [[ -z "$*" ]]; then help exit 1 fi declare -a files pattern_results for pattern in "$@"; do readarray -d $'\n' -t pattern_results <<< "$(fd "${fd_opts[@]}" "${typeopts[@]}" "$pattern")" files+=("${pattern_results[@]}") done # If nothing was found if [[ -z "${files[*]}" ]]; then color_output 'No files found, exiting' 'yellow' exit 1 fi # Sort list of paths before printing readarray -t paths < <(printf '%s\n' "${files[@]}" | sort --unique) # Print list of files found matching criteria i=1 for p in "${paths[@]}"; do printf '%s. %s\n' "$(color_output $i 'white')" "$(color_path "$p")" i=$((i + 1)) done # Padding between files and prompt color_output '' reset read -r -n 1 -p 'Would you like to delete these files? [y/N]: ' user_response # Padding between prompt and output echo '' if [[ "$user_response" =~ (y|Y) ]]; then for p in "${paths[@]}"; do rm "${rm_opts[@]}" -- "$p" || printf '%s %s\n' "$(color_output "Unable to remove path:" 'red')" "$(color_path "$p")" done fi