improved and modernised file functions

This commit is contained in:
fatalerrors
2026-04-01 15:27:45 +02:00
parent 6b85556a53
commit c039ab6ea0

View File

@@ -35,32 +35,85 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# expandlist : treat wildcards in a file/directory list # Expand wildcards in a file/directory list and quote the results
# Usage: expandlist <item1 [item2 ... itemN]> # Usage: expandlist [options] <item1 [item2 ... itemN]>
expandlist() expandlist()
{ {
if [[ "$1" == "-h" || "$1" == "--help" ]]; then local separator=" "
printf "expandlist: Wraps a list of items in double quotes.\n\n" local PARSED
printf "Usage: expandlist <item1 [item2 ... itemN]>\n\n"
printf "Options:\n" PARSED=$(getopt -o hs:n --long help,separator:,newline -n 'expandlist' -- "$@")
printf "\t-h, --help\t\tDisplay this help screen\n" if [[ $? -ne 0 ]]; then
return 0 disp E "Invalid options, use \"expandlist --help\" to display usage."
fi 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] <item1 [item2 ... itemN]>\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 item in "$@"; do
for content in "$item"; do local expanded=()
result+="\"$content\" "
# 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
done done
echo $result
shopt -u nullglob
printf '%s\n' "$result"
} }
export -f expandlist 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]] # Usage: clean [options] [directory1] [...[directoryX]]
# Options: # Options:
# -h, --help: display help screen # -h, --help: display help screen
@@ -85,7 +138,7 @@ clean()
while true; do while true; do
case "$1" in case "$1" in
-r|--recurs) -r|--recurs)
local recursive=1 recursive=1
shift shift
;; ;;
-h|--help) -h|--help)
@@ -100,11 +153,11 @@ clean()
return 0 return 0
;; ;;
-s|--shell) -s|--shell)
local outshell=1 outshell=1
shift shift
;; ;;
-f|--force) -f|--force)
local force=1 force=1
shift shift
;; ;;
--) --)
@@ -114,6 +167,7 @@ clean()
*) *)
disp E "Invalid parameter, use \"clean --help\" to display options list" disp E "Invalid parameter, use \"clean --help\" to display options list"
return 1 return 1
;;
esac esac
done done
@@ -121,21 +175,24 @@ clean()
local dirlist=("$@") local dirlist=("$@")
[[ ${#dirlist[@]} -eq 0 ]] && dirlist=(".") [[ ${#dirlist[@]} -eq 0 ]] && dirlist=(".")
local findopt=() rmopt local findopt=() rmopt=()
[[ ! $recursive ]] && findopt=(-maxdepth 1) (( ! recursive )) && findopt=(-maxdepth 1)
[[ ! $force ]] && rmopt="-i" (( ! force )) && rmopt=(-i)
unset recursive force
for dir in $dirlist; do 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 find "$dir" "${findopt[@]}" -type f \( -name "*~" -o -name "#*#" -o -name "*.bak" -o -name ".~*#" \) -print0 |
if [[ ! $outshell ]]; then while IFS= read -r -d '' f; do
rm $rmopt $f if (( outshell )); then
else if (( ${#rmopt[@]} )); then
echo "rm $rmopt $f" printf 'rm %s -- "%s"\n' "${rmopt[*]}" "$f"
fi else
done printf 'rm -- "%s"\n' "$f"
fi
else
rm "${rmopt[@]}" -- "$f"
fi
done
done done
unset outshell dirlist dellist findopt rmopt
} }
export -f clean 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] # Usage: rmspc [options]
# Options: # Options:
# -h, --help: display help screen # -h, --help: display help screen
@@ -178,8 +235,11 @@ export -f mcd
# -s, --shell: do nothing and display commands that would be executed # -s, --shell: do nothing and display commands that would be executed
rmspc() rmspc()
{ {
local lst="" local recurs=0 verb=0 shell=0
local substchar="_" substchar_set=0
local mvopt=()
local PARSED local PARSED
PARSED=$(getopt -o hr:c::vs --long help,recursive,subst-char::,verbose,shell -n 'rmspc' -- "$@") PARSED=$(getopt -o hr:c::vs --long help,recursive,subst-char::,verbose,shell -n 'rmspc' -- "$@")
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"rmspc --help\" to display usage." disp E "Invalid options, use \"rmspc --help\" to display usage."
@@ -203,27 +263,20 @@ rmspc()
return 0 return 0
;; ;;
-r|--recursive) -r|--recursive)
local recurs=1 recurs=1
shift shift
;; ;;
-c|--subst-char) -c|--subst-char)
# Handle optional argument for short/long options substchar_set=1
case "$2" in substchar="$2"
"")
substchar=""
;;
*)
substchar="$2"
;;
esac
shift 2 shift 2
;; ;;
-v|--verbose) -v|--verbose)
local verb=1 verb=1
shift shift
;; ;;
-s|--shell) -s|--shell)
local shell=1 shell=1
shift shift
;; ;;
--) --)
@@ -232,49 +285,58 @@ rmspc()
;; ;;
*) *)
disp E "Invalid parameter, use \"rmspc --help\" to display options list" disp E "Invalid parameter, use \"rmspc --help\" to display options list"
echo
return 1 return 1
;; ;;
esac esac
done done
[[ ! $substchar ]] && substchar="_" [[ "$substchar" == "none" ]] && substchar=""
[[ $substchar == "none" ]] && local substchar="" (( verb )) && mvopt=(-v)
[[ $verb ]] && local mvopt="-v"
shopt -s nullglob shopt -s nullglob
for f in *; do for f in *; do
[[ $recurs ]] && [[ -d "$f" ]] && ( if (( recurs )) && [[ -d "$f" ]]; then
[[ $verb ]] && disp I "Entering directory $(pwd)/$f ..." (
local lastdir=$f local lastdir=$f
pushd "$f" >/dev/null (( verb )) && disp I "Entering directory $(pwd)/$f ..."
rmspc ${recurs:+-r} ${substchar:+-c "$substchar"} ${verb:+-v} ${shell:+-s} pushd "$f" >/dev/null || exit 1
popd >/dev/null
[[ $verb ]] && disp I "Leaving directory $(pwd)/$lastdir" if (( substchar_set )); then
unset lastdir 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 if [[ "$f" == *" "* ]]; then
local newf="${f// /${substchar}}" local newf="${f// /${substchar}}"
[[ "$f" == "$newf" ]] && continue # protection but should never happen [[ "$f" == "$newf" ]] && continue
if [[ -n $shell ]]; then if (( shell )); then
echo "mv ${mvopt:+$mvopt }\"$f\" \"$newf\"" if (( ${#mvopt[@]} )); then
printf 'mv %s -- "%s" "%s"\n' "${mvopt[*]}" "$f" "$newf"
else
printf 'mv -- "%s" "%s"\n' "$f" "$newf"
fi
else else
mv ${mvopt:+$mvopt} "$f" "$newf" || { mv "${mvopt[@]}" -- "$f" "$newf" || {
disp E "Failed renaming \"$f\" to \"$newf\"." disp E "Failed renaming \"$f\" to \"$newf\"."
continue continue
} }
fi fi
fi fi
done done
unset lst substchar verb shell newf command mvopt shopt -u nullglob
} }
export -f rmspc export -f rmspc
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# display stats about a file structure # Display statistics about a file tree
# Usage: file_stats [options] [path] # Usage: file_stats [options] [path]
# Options: # Options:
# -H, --human Human readable sizes\n" # -H, --human Human readable sizes\n"
@@ -290,7 +352,7 @@ export -f rmspc
# --max [size] Maximum size (e.g., 100M) # --max [size] Maximum size (e.g., 100M)
file_stats() 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 path="." show_all=1 ext_filter="" ext_list="" min_size="" max_size=""
local PARSED 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 # Prepare find filters
local find_cmd=(find "$path" -type f) local find_cmd=(find "$path" -type f)
# Extension simple # Single extension filter
if [[ -n "$ext_filter" ]]; then if [[ -n "$ext_filter" ]]; then
find_cmd+=(-iname "*.$ext_filter") find_cmd+=(-iname "*.$ext_filter")
fi fi
# Extension liste # Extension list filter
if [[ -n "$ext_list" ]]; then if [[ -n "$ext_list" ]]; then
IFS=',' read -ra exts <<< "$ext_list" IFS=',' read -ra exts <<< "$ext_list"
find_cmd+=('(') 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+=(')') find_cmd+=(')')
fi fi
# Taille min/max (à évaluer en octets) # Minimum/maximum size filters (evaluated in bytes)
if [[ -n "$min_size" ]]; then if [[ -n "$min_size" ]]; then
find_cmd+=(-size +"$(numfmt --from=iec "$min_size")"c) find_cmd+=(-size +"$(numfmt --from=iec "$min_size")"c)
fi 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) find_cmd+=(-size -"$(( $(numfmt --from=iec "$max_size") + 1 ))"c)
fi fi
# Exécution # Execution
"${find_cmd[@]}" -printf "%s\n" 2>/dev/null | sort -n | \ "${find_cmd[@]}" -printf "%s\n" 2>/dev/null | sort -n | \
awk -v human="$human" -v details="$details" -v only_avg="$only_avg" \ awk -v human="$human" -v details="$details" -v only_avg="$only_avg" \
-v only_med="$only_med" -v only_count="$only_count" \ -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) # -l : limit : number of files to return (default is 10)
findbig() findbig()
{ {
local details=0 limit=10 no_change=0 one_fs=0 local details=0 limit=10 one_fs=0
local PARSED 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 if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"findbig --help\" to display usage." disp E "Invalid options, use \"findbig --help\" to display usage."
return 1 return 1
@@ -538,14 +600,18 @@ findbig()
details=1 details=1
shift shift
;; ;;
-n|--no-change)
no_change=1
shift
;;
-l|--limit) -l|--limit)
limit="$2" limit="$2"
[[ "$limit" =~ ^[0-9]+$ ]] || {
disp E "Invalid limit: must be a positive integer."
return 1
}
shift 2 shift 2
;; ;;
-x|--one-fs)
one_fs=1
shift
;;
--) --)
shift shift
break break
@@ -560,14 +626,17 @@ findbig()
local dir="${1:-.}" local dir="${1:-.}"
# Prepare find arguments in an array for cleaner handling # Prepare find arguments in an array for cleaner handling
local find_args=("-L" "$dir" "-type" "f") local find_args=(-L "$dir")
(( one_fs )) && find_args+=("-xdev") (( one_fs )) && find_args+=(-xdev)
find_args+=(-type f)
# Logic: find files, print size and path, sort numeric reverse, take N # Logic: find files, print size and path, sort numeric reverse, take N
if (( details )); then if (( details )); then
find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" | while read -r size path; do find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" |
ls -ld "$path" while IFS= read -r line; do
done local path="${line#* }"
ls -ld -- "$path"
done
else else
find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit"
fi fi
@@ -586,7 +655,7 @@ export -f findbig
# --delete : delete empty files and display their paths # --delete : delete empty files and display their paths
findzero() findzero()
{ {
local delete=0 details=0 one_fs=0 no_change=0 local delete=0 details=0 one_fs=0
local PARSED local PARSED
# o: options, long: long equivalents # o: options, long: long equivalents
@@ -660,7 +729,7 @@ export -f findzero
# --delete : delete dead links and display their paths # --delete : delete dead links and display their paths
finddead() finddead()
{ {
local delete=0 details=0 one_fs=0 no_change=0 local delete=0 details=0 one_fs=0
local PARSED local PARSED
PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'finddead' -- "$@") PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'finddead' -- "$@")