Files
profile/profile.d/filefct.sh
Geoffray Levasseur-Brandin f944271488 added file_stats function
2025-06-19 14:32:29 +02:00

341 lines
11 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# Copyright (c) 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
# Protected by the BSD3 license. Please read bellow for details.
#
# * Redistribution and use in source and binary forms,
# * with or without modification, are permitted provided
# * that the following conditions are met:
# *
# * Redistributions of source code must retain the above
# * copyright notice, this list of conditions and the
# * following disclaimer.
# *
# * Redistributions in binary form must reproduce the above
# * copyright notice, this list of conditions and the following
# * disclaimer in the documentation and/or other materials
# * provided with the distribution.
# *
# * Neither the name of the copyright holder nor the names
# * of any other contributors may be used to endorse or
# * promote products derived from this software without
# * specific prior written permission.
# *
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# * OF SUCH DAMAGE.
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# expandlist : treat wildcards in a file/directory list
# ------------------------------------------------------------------------------
expandlist()
{
local result=""
for item in "$1"; do
for content in "$item"; do
result+="\"$content\" "
done
done
echo $result
}
# ------------------------------------------------------------------------------
# Clean a directory or a tree from temporary or backup files
# ------------------------------------------------------------------------------
clean()
{
for opt in $@; do
case $opt in
"-r" | "--recurs")
local recursive=1
;;
"-h" | "--help")
echo "clean: erase backup files in the given directories."
echo
echo "Usage: clean [option] [directory1] [...[directoryX]]"
echo
echo "Options:"
echo " -h, --help Display that help screen"
echo " -r, --recurs Do a recursive cleaning"
echo " -f, --force Do not ask for confirmation (use with care)"
echo " -s, --shell Do nothing and display what will be executed"
echo
return 0
;;
"-s" | "--shell")
local outshell=1
;;
"-f" | "--force")
local force=1
;;
"-"*)
disp E "Invalid option, use \"clean --help\" to display usage."
echo
return 1
;;
*)
local dirlist="$dirlist $opt"
;;
esac
done
[[ ! $dirlist ]] && local dirlist=$(pwd)
[[ ! $recursive ]] && local findopt="-maxdepth 1"
[[ ! $force ]] && local rmopt="-i"
unset recursive force
for dir in $dirlist; do
local dellist=$(find "$dir" $findopt -type f -name "*~" -o -name "#*#" \
-o -name "*.bak" -o -name ".~*#")
for f in $dellist; do
if [[ ! $outshell ]]; then
rm $rmopt $f
else
echo "rm $rmopt $f"
fi
done
done
unset outshell dirlist dellist findopt rmopt
}
export -f clean
# ------------------------------------------------------------------------------
# Create a directory then goes inside
# ------------------------------------------------------------------------------
mcd()
{
if [[ ! $# -eq 1 ]]; then
disp E "Create a directory then goes inside."
disp E "Usage: mcd <directory>"
return 1
fi
mkdir -pv "$1" && cd "$1" || echo "Failed create or change directory."
}
export -f mcd
# ------------------------------------------------------------------------------
# Rename all files in current directory to replace spaces with _
# ------------------------------------------------------------------------------
rmspc()
{
local lst=""
for opt in $@; do
case $opt in
"-h" | "--help")
echo "rmspc: remove spaces from all filenames in current directories"
echo
echo "Usage: rmspc [option]"
echo
echo "Options:"
echo " -h, --help Display that help screen"
echo " -r, --recursive Treat subdirectories of the given directory"
echo " -c, --subst-char Change the replacement character (default is underscore)"
echo " -v, --verbose Display more details (recursive mode only)"
echo " -s, --shell Do nothing and display commands that would be executed"
echo
echo "Note: if the --subst-char option is given without parameters, spaces will be"
echo " replaced with nothing (concatenation)."
echo
return 0
;;
"-r" | "--recursive")
local recurs=1
;;
"-c"?* | "--subst-char"?*)
if [[ $(echo $opt | grep "=") ]]; then
local substchar=$(echo "$opt" | cut -f 2- -d '=')
else
local substchar='none'
fi
;;
"-v" | "--verbose")
local verb=1
;;
"-s" | "--shell")
local shell=1
;;
*)
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"
for f in *; do
[[ $recurs ]] && [[ -d "$f" ]] && (
[[ $verb ]] && disp I "Entering directory $(pwd)/$f ..."
local lastdir=$f
pushd "$f" >/dev/null
rmspc $@
popd >/dev/null
[[ $verb ]] && disp I "Leaving directory $(pwd)/$lastdir"
unset lastdir
)
if [[ $(echo $f | grep " ") ]]; then
local newf="${f// /${substchar}}"
local command="mv $mvopt \"$f\" \"$newf\""
if [[ $shell ]]; then
echo $command
else
$command
fi
fi
done
unset lst substchar verb shell newf command mvopt
}
export -f rmspc
# ------------------------------------------------------------------------------
# Rename all files in current directory to replace spaces with _
# ------------------------------------------------------------------------------
file_stats()
{
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 OPTIND opt
# Analyse options
while [[ "$1" =~ ^- ]]; do
case "$1" in
-H) human=1 ;;
-d) details=1 ;;
-m) only_avg=1; show_all=0 ;;
-M) only_med=1; show_all=0 ;;
-c) only_count=1; show_all=0 ;;
-t) only_total=1; show_all=0 ;;
-a) human=1; details=1 ;;
-x) ext_filter="${2#.}"; shift ;;
-X) ext_list="${2}"; shift ;;
--min) min_size="$2"; shift ;;
--max) max_size="$2"; shift ;;
--) shift; break ;;
-*) echo "Usage: file_stats [-h] [-d] [-mMctaxX --min N --max N] [path]"; return 1 ;;
esac
shift
done
[ -n "$1" ] && path="$1"
# Prepare find filters
local find_cmd=(find "$path" -type f)
# Extension simple
if [[ -n "$ext_filter" ]]; then
find_cmd+=(-iname "*.$ext_filter")
fi
# Extension liste
if [[ -n "$ext_list" ]]; then
IFS=',' read -ra exts <<< "$ext_list"
find_cmd+=('(')
for i in "${!exts[@]}"; do
[[ $i -ne 0 ]] && find_cmd+=(-o)
find_cmd+=(-iname "*.${exts[$i]}")
done
find_cmd+=(')')
fi
# Taille min/max (à évaluer en octets)
if [[ -n "$min_size" ]]; then
find_cmd+=(-size +"$(numfmt --from=iec "$min_size")"c)
fi
if [[ -n "$max_size" ]]; then
find_cmd+=(-size -"$(( $(numfmt --from=iec "$max_size") + 1 ))"c)
fi
# Exécution
"${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" -v only_total="$only_total" -v show_all="$show_all" -v path="$path" '
function human_readable(x) {
split("B KiB MiB GiB TiB", units)
for (i=1; x>=1024 && i<5; i++) x /= 1024
return sprintf("%.2f %s", x, units[i])
}
{
sizes[NR] = $1
total += $1
if (min == "" || $1 < min) min = $1
if (max == "" || $1 > max) max = $1
if ($1 == 0) bucket[0]++
else {
b = int(log($1)/log(1024))
bucket[b]++
}
}
END {
count = NR
if (count == 0) {
print "Aucun fichier trouvé."; exit
}
moyenne = total / count
if (count % 2 == 1)
mediane = sizes[(count + 1) / 2]
else
mediane = (sizes[count / 2] + sizes[count / 2 + 1]) / 2
function out(label, val) {
if (human) val = human_readable(val)
printf "%-20s : %s\n", label, val
}
if (only_avg) out("Taille moyenne", moyenne)
else if (only_med) out("Taille médiane", mediane)
else if (only_count) printf "Nombre de fichiers : %d\n", count
else if (only_total) out("Taille totale", total)
else {
if (show_all || human || details) {
printf "Statistiques sur \"%s\"\n", path
printf "-------------------------\n"
}
out("Nombre de fichiers", count)
out("Taille totale", total)
out("Taille moyenne", moyenne)
out("Taille médiane", mediane)
out("Taille minimale", min)
out("Taille maximale", max)
}
if (details) {
print "\nHistogramme des tailles :"
for (i = 0; i in bucket; i++) {
low = 2^i
high = 2^(i+1)
if (i == 0)
label = sprintf("%4s %4s", "0", "1K")
else
label = sprintf("%4s %4s", human_readable(low), human_readable(high))
printf "%-20s : %5d fichiers\n", label, bucket[i]
}
}
}'
}
# ------------------------------------------------------------------------------
# EOF