From c039ab6ea09139417042f623fe48c23088ee7c61 Mon Sep 17 00:00:00 2001 From: fatalerrors Date: Wed, 1 Apr 2026 15:27:45 +0200 Subject: [PATCH] improved and modernised file functions --- profile.d/filefct.sh | 231 ++++++++++++++++++++++++++++--------------- 1 file changed, 150 insertions(+), 81 deletions(-) diff --git a/profile.d/filefct.sh b/profile.d/filefct.sh index aac26ac..caeddc6 100644 --- a/profile.d/filefct.sh +++ b/profile.d/filefct.sh @@ -35,32 +35,85 @@ # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ -# expandlist : treat wildcards in a file/directory list -# Usage: expandlist +# Expand wildcards in a file/directory list and quote the results +# Usage: expandlist [options] expandlist() { - if [[ "$1" == "-h" || "$1" == "--help" ]]; then - printf "expandlist: Wraps a list of items in double quotes.\n\n" - printf "Usage: expandlist \n\n" - printf "Options:\n" - printf "\t-h, --help\t\tDisplay this help screen\n" - return 0 - fi + local separator=" " + local PARSED + + PARSED=$(getopt -o hs:n --long help,separator:,newline -n 'expandlist' -- "$@") + if [[ $? -ne 0 ]]; then + disp E "Invalid options, use \"expandlist --help\" to display usage." + return 1 + fi + eval set -- "$PARSED" + + while true; do + case "$1" in + -h|--help) + printf "expandlist: expand globs and wrap matched items in double quotes.\n\n" + printf "Usage: expandlist [options] \n\n" + printf "Options:\n" + printf "\t-h, --help\t\tDisplay this help screen\n" + printf "\t-s, --separator SEP\tSet output separator (default: space)\n" + printf "\t-n, --newline\t\tUse a newline as separator\n" + return 0 + ;; + -s|--separator) + separator="$2" + shift 2 + ;; + -n|--newline) + separator=$'\n' + shift + ;; + --) + shift + break + ;; + *) + disp E "Invalid options, use \"expandlist --help\" to display usage." + return 1 + ;; + esac + done + + local item="" result="" matched=0 + shopt -s nullglob - local result="" for item in "$@"; do - for content in "$item"; do - result+="\"$content\" " + local expanded=() + + # True glob expansion when wildcards are present. + if [[ "$item" == *'*'* || "$item" == *'?'* || "$item" == *'['* ]]; then + expanded=( $item ) + else + expanded=( "$item" ) + fi + + if [[ ${#expanded[@]} -eq 0 ]]; then + continue + fi + + for content in "${expanded[@]}"; do + if (( matched )); then + result+="$separator" + fi + result+="\"$content\"" + matched=1 done done - echo $result + + shopt -u nullglob + printf '%s\n' "$result" } export -f expandlist # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ -# Clean a directory or a tree from temporary or backup files +# Clean a directory tree from temporary or backup files # Usage: clean [options] [directory1] [...[directoryX]] # Options: # -h, --help: display help screen @@ -85,7 +138,7 @@ clean() while true; do case "$1" in -r|--recurs) - local recursive=1 + recursive=1 shift ;; -h|--help) @@ -100,11 +153,11 @@ clean() return 0 ;; -s|--shell) - local outshell=1 + outshell=1 shift ;; -f|--force) - local force=1 + force=1 shift ;; --) @@ -114,6 +167,7 @@ clean() *) disp E "Invalid parameter, use \"clean --help\" to display options list" return 1 + ;; esac done @@ -121,21 +175,24 @@ clean() local dirlist=("$@") [[ ${#dirlist[@]} -eq 0 ]] && dirlist=(".") - local findopt=() rmopt - [[ ! $recursive ]] && findopt=(-maxdepth 1) - [[ ! $force ]] && rmopt="-i" - unset recursive force + local findopt=() rmopt=() + (( ! recursive )) && findopt=(-maxdepth 1) + (( ! force )) && rmopt=(-i) - for dir in $dirlist; do - find "$dir" "${findopt[@]}" -type f \( -name "*~" -o -name "#*#" -o -name "*.bak" -o -name ".~*#" \) -print0 | while IFS= read -r -d '' f; do - if [[ ! $outshell ]]; then - rm $rmopt $f - else - echo "rm $rmopt $f" - fi - done + for dir in "${dirlist[@]}"; do + find "$dir" "${findopt[@]}" -type f \( -name "*~" -o -name "#*#" -o -name "*.bak" -o -name ".~*#" \) -print0 | + while IFS= read -r -d '' f; do + if (( outshell )); then + if (( ${#rmopt[@]} )); then + printf 'rm %s -- "%s"\n' "${rmopt[*]}" "$f" + else + printf 'rm -- "%s"\n' "$f" + fi + else + rm "${rmopt[@]}" -- "$f" + fi + done done - unset outshell dirlist dellist findopt rmopt } export -f clean # ------------------------------------------------------------------------------ @@ -168,7 +225,7 @@ export -f mcd # ------------------------------------------------------------------------------ -# Rename all files in current directory to replace spaces with _ +# Rename files and directories to replace spaces with another character # Usage: rmspc [options] # Options: # -h, --help: display help screen @@ -178,8 +235,11 @@ export -f mcd # -s, --shell: do nothing and display commands that would be executed rmspc() { - local lst="" + local recurs=0 verb=0 shell=0 + local substchar="_" substchar_set=0 + local mvopt=() local PARSED + PARSED=$(getopt -o hr:c::vs --long help,recursive,subst-char::,verbose,shell -n 'rmspc' -- "$@") if [[ $? -ne 0 ]]; then disp E "Invalid options, use \"rmspc --help\" to display usage." @@ -203,27 +263,20 @@ rmspc() return 0 ;; -r|--recursive) - local recurs=1 + recurs=1 shift ;; -c|--subst-char) - # Handle optional argument for short/long options - case "$2" in - "") - substchar="" - ;; - *) - substchar="$2" - ;; - esac + substchar_set=1 + substchar="$2" shift 2 ;; -v|--verbose) - local verb=1 + verb=1 shift ;; -s|--shell) - local shell=1 + shell=1 shift ;; --) @@ -232,49 +285,58 @@ rmspc() ;; *) disp E "Invalid parameter, use \"rmspc --help\" to display options list" - echo return 1 ;; esac done - [[ ! $substchar ]] && substchar="_" - [[ $substchar == "none" ]] && local substchar="" - [[ $verb ]] && local mvopt="-v" + [[ "$substchar" == "none" ]] && substchar="" + (( verb )) && mvopt=(-v) shopt -s nullglob for f in *; do - [[ $recurs ]] && [[ -d "$f" ]] && ( - [[ $verb ]] && disp I "Entering directory $(pwd)/$f ..." - local lastdir=$f - pushd "$f" >/dev/null - rmspc ${recurs:+-r} ${substchar:+-c "$substchar"} ${verb:+-v} ${shell:+-s} - popd >/dev/null - [[ $verb ]] && disp I "Leaving directory $(pwd)/$lastdir" - unset lastdir - ) + if (( recurs )) && [[ -d "$f" ]]; then + ( + local lastdir=$f + (( verb )) && disp I "Entering directory $(pwd)/$f ..." + pushd "$f" >/dev/null || exit 1 + + if (( substchar_set )); then + rmspc ${recurs:+-r} -c "$substchar" ${verb:+-v} ${shell:+-s} + else + rmspc ${recurs:+-r} ${verb:+-v} ${shell:+-s} + fi + + popd >/dev/null || exit 1 + (( verb )) && disp I "Leaving directory $(pwd)/$lastdir" + ) + fi if [[ "$f" == *" "* ]]; then local newf="${f// /${substchar}}" - [[ "$f" == "$newf" ]] && continue # protection but should never happen - if [[ -n $shell ]]; then - echo "mv ${mvopt:+$mvopt }\"$f\" \"$newf\"" + [[ "$f" == "$newf" ]] && continue + if (( shell )); then + if (( ${#mvopt[@]} )); then + printf 'mv %s -- "%s" "%s"\n' "${mvopt[*]}" "$f" "$newf" + else + printf 'mv -- "%s" "%s"\n' "$f" "$newf" + fi else - mv ${mvopt:+$mvopt} "$f" "$newf" || { + mv "${mvopt[@]}" -- "$f" "$newf" || { disp E "Failed renaming \"$f\" to \"$newf\"." continue } fi fi done - unset lst substchar verb shell newf command mvopt + shopt -u nullglob } export -f rmspc # ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------ -# display stats about a file structure +# Display statistics about a file tree # Usage: file_stats [options] [path] # Options: # -H, --human Human readable sizes\n" @@ -290,7 +352,7 @@ export -f rmspc # --max [size] Maximum size (e.g., 100M) file_stats() { -local human=0 details=0 only_avg=0 only_med=0 only_count=0 only_total=0 + local human=0 details=0 only_avg=0 only_med=0 only_count=0 only_total=0 local path="." show_all=1 ext_filter="" ext_list="" min_size="" max_size="" local PARSED @@ -387,12 +449,12 @@ local human=0 details=0 only_avg=0 only_med=0 only_count=0 only_total=0 # Prepare find filters local find_cmd=(find "$path" -type f) - # Extension simple + # Single extension filter if [[ -n "$ext_filter" ]]; then find_cmd+=(-iname "*.$ext_filter") fi - # Extension liste + # Extension list filter if [[ -n "$ext_list" ]]; then IFS=',' read -ra exts <<< "$ext_list" find_cmd+=('(') @@ -403,7 +465,7 @@ local human=0 details=0 only_avg=0 only_med=0 only_count=0 only_total=0 find_cmd+=(')') fi - # Taille min/max (à évaluer en octets) + # Minimum/maximum size filters (evaluated in bytes) if [[ -n "$min_size" ]]; then find_cmd+=(-size +"$(numfmt --from=iec "$min_size")"c) fi @@ -411,7 +473,7 @@ local human=0 details=0 only_avg=0 only_med=0 only_count=0 only_total=0 find_cmd+=(-size -"$(( $(numfmt --from=iec "$max_size") + 1 ))"c) fi - # Exécution + # Execution "${find_cmd[@]}" -printf "%s\n" 2>/dev/null | sort -n | \ awk -v human="$human" -v details="$details" -v only_avg="$only_avg" \ -v only_med="$only_med" -v only_count="$only_count" \ @@ -512,10 +574,10 @@ export -f file_stats # -l : limit : number of files to return (default is 10) findbig() { - local details=0 limit=10 no_change=0 one_fs=0 + local details=0 limit=10 one_fs=0 local PARSED - PARSED=$(getopt -o hd:l:x --long help,details,one-fs,limit: -n 'findbig' -- "$@") + PARSED=$(getopt -o hdl:x --long help,details,limit:,one-fs -n 'findbig' -- "$@") if [[ $? -ne 0 ]]; then disp E "Invalid options, use \"findbig --help\" to display usage." return 1 @@ -538,14 +600,18 @@ findbig() details=1 shift ;; - -n|--no-change) - no_change=1 - shift - ;; -l|--limit) limit="$2" + [[ "$limit" =~ ^[0-9]+$ ]] || { + disp E "Invalid limit: must be a positive integer." + return 1 + } shift 2 ;; + -x|--one-fs) + one_fs=1 + shift + ;; --) shift break @@ -560,14 +626,17 @@ findbig() local dir="${1:-.}" # Prepare find arguments in an array for cleaner handling - local find_args=("-L" "$dir" "-type" "f") - (( one_fs )) && find_args+=("-xdev") + local find_args=(-L "$dir") + (( one_fs )) && find_args+=(-xdev) + find_args+=(-type f) # Logic: find files, print size and path, sort numeric reverse, take N if (( details )); then - find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" | while read -r size path; do - ls -ld "$path" - done + find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" | + while IFS= read -r line; do + local path="${line#* }" + ls -ld -- "$path" + done else find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" fi @@ -586,7 +655,7 @@ export -f findbig # --delete : delete empty files and display their paths findzero() { - local delete=0 details=0 one_fs=0 no_change=0 + local delete=0 details=0 one_fs=0 local PARSED # o: options, long: long equivalents @@ -660,7 +729,7 @@ export -f findzero # --delete : delete dead links and display their paths finddead() { - local delete=0 details=0 one_fs=0 no_change=0 + local delete=0 details=0 one_fs=0 local PARSED PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'finddead' -- "$@")