2017-04-20 23:20:18 +04:00
|
|
|
#!/bin/bash
|
|
|
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
source "$(dirname "$0")/lib/ulib/ulib" || exit 1
|
2017-09-14 22:21:53 +04:00
|
|
|
urequire DEFAULTS multiconf
|
2017-04-20 23:20:18 +04:00
|
|
|
|
|
|
|
function display_help() {
|
|
|
|
uecho "$scriptname: classer des fichiers selon certains règles
|
|
|
|
|
2017-04-22 09:49:25 +04:00
|
|
|
Les règles sont spécifiées dans le fichier ~/etc/default/ufile
|
|
|
|
|
|
|
|
Dans ce fichier, le tableau RULES contient des règles qui sont chacune de la
|
|
|
|
forme pattern:destdir[:renamef]
|
|
|
|
|
|
|
|
- pattern est au format glob et identifie les fichiers auxquels s'applique la
|
2017-10-17 22:45:47 +04:00
|
|
|
règle, sauf si la chaine commence par / auquel cas il s'agit d'une expression
|
|
|
|
régulière reconnue par awk. Par exemple, les deux patterns suivants sont
|
|
|
|
équivalents:
|
|
|
|
[ab]cd*.pdf
|
|
|
|
/[ab]cd.*\\\\.pdf\$
|
2017-04-22 09:49:25 +04:00
|
|
|
- destdir est le répertoire de destination dans lequel classer le fichier
|
|
|
|
- renamef est une fonction qui permet de supporter le renommage d'un fichier
|
|
|
|
lors de son classement. Sa signature est 'renamef filename pf destdir' où
|
|
|
|
filename est le nom du fichier source, pf son chemin complet et destdir la
|
|
|
|
valeur de destdir mentionnée dans la règle.
|
|
|
|
La fonction doit définir la variable dest qui est le nouveau nom. Si la
|
|
|
|
fonction retourne un code d'erreur autre que zéro, la règle est ignorée.
|
|
|
|
Si le nouveau nom contient un chemin, destdir est ignoré et le fichier est
|
2017-04-25 08:27:09 +04:00
|
|
|
déplacé dans le répertoire spécifié avec le nom spécifié. Si dest est un
|
|
|
|
tableau avec plusieurs destinations, alors le fichier est copié en plusieurs
|
|
|
|
fois.
|
2017-04-22 10:08:13 +04:00
|
|
|
Si dest est de la forme [user@]host:path alors le fichier est copié par scp
|
|
|
|
sur l'hôte spécifié vers la destination spécifiée, sauf si l'hôte courant est
|
|
|
|
déjà celui mentionné dans la valeur, auquel cas la copie est faite directement
|
|
|
|
dans le répertoire spécifié.
|
2017-04-22 10:22:18 +04:00
|
|
|
Si le user et l'hôte courant sont déjà à la valeur spécifiée, alors la copie
|
2017-04-25 08:27:09 +04:00
|
|
|
est faite en local sans utiliser scp sauf si l'option --force-scp est utilisée
|
|
|
|
Le chemin spécifié, en local ou distant, est toujours le chemin complet vers
|
|
|
|
le fichier destination. Si on veut copier le fichier sans le renommer vers un
|
|
|
|
répertoire, il faut mettre un slash e.g destdir/ ou user@host:destdir/
|
|
|
|
variables pouvant être définies mais non documentées: interaction(=-i)
|
2017-04-20 23:20:18 +04:00
|
|
|
|
|
|
|
USAGE
|
|
|
|
$scriptname [options] <files...>
|
2017-09-22 21:14:16 +04:00
|
|
|
$scriptname [options] -r <files|dirs...>
|
2017-04-20 23:20:18 +04:00
|
|
|
|
|
|
|
OPTIONS
|
2017-04-22 09:49:25 +04:00
|
|
|
-c, --config CONFIG
|
|
|
|
Utiliser le fichier de configuration spécifié au lieu de la valeur par
|
2017-12-17 11:46:58 +04:00
|
|
|
défaut ~/etc/default/ufile et ~/etc/ufile.d/*.conf
|
|
|
|
-C, --other-configs
|
|
|
|
Charger les fichiers ~/etc/ufile.d/*.conf en plus du fichier spécifié
|
|
|
|
avec --config. Cette option est ignorée si --config n'est pas utilisé.
|
2017-04-25 08:27:09 +04:00
|
|
|
--file
|
|
|
|
Classer les fichiers spécifiés. C'est l'action par défaut
|
2017-07-19 23:12:06 +04:00
|
|
|
--force-cp
|
|
|
|
Spécifier le mode de classement des fichiers. Par défaut, le fichier est
|
|
|
|
déplacé dans la destination s'il s'agit d'un classement local, ou copié
|
|
|
|
s'il s'agit d'un classement distant. Avec --force-cp, le fichier est
|
|
|
|
systématiquement copié dans la destination.
|
2017-04-25 08:27:09 +04:00
|
|
|
-S, --ssh SSH
|
|
|
|
S'il faut classer sur un hôte distant avec scp, utiliser le programme
|
|
|
|
spécifié pour la connexion par ssh
|
|
|
|
--force-scp
|
|
|
|
Toujours utiliser scp pour une copie distante. Par défaut s'il est
|
|
|
|
déterminé que l'hôte distant est en réalité l'hôte courant, alors la
|
|
|
|
copie est effectuée directement.
|
2017-10-28 11:25:11 +04:00
|
|
|
-f, --force
|
|
|
|
Si le fichier destination existe, alors l'écraser sans confirmation.
|
|
|
|
Cette option est ignorée pour un classement distant.
|
2017-09-22 21:14:16 +04:00
|
|
|
-r, --recursive
|
|
|
|
Classer récursivement tous les fichiers d'un répertoire. Sans cette
|
|
|
|
option, il n'est pas autorisé de fournir un répertoire comme argument.
|
2017-04-20 23:20:18 +04:00
|
|
|
-n, --fake
|
2017-04-21 10:12:25 +04:00
|
|
|
Afficher les opérations qui seraient faites
|
|
|
|
-l, --list
|
|
|
|
Lister les règles définies
|
|
|
|
-e, --edit
|
2017-04-22 09:49:25 +04:00
|
|
|
Lancer un éditeur sur le fichier de configuration"
|
|
|
|
}
|
|
|
|
|
|
|
|
function joinp() {
|
|
|
|
# afficher le chemin $1/$2
|
|
|
|
local pf="$1"
|
|
|
|
[ -n "$2" -a "${pf%/}" == "$pf" ] && pf="$pf/"
|
|
|
|
pf="$pf${2#/}"
|
|
|
|
echo "$pf"
|
2017-04-20 23:20:18 +04:00
|
|
|
}
|
|
|
|
|
2017-04-25 08:27:09 +04:00
|
|
|
function __check_destdir() {
|
|
|
|
local destdir="$1" rule="$2"
|
|
|
|
if [ -z "$destdir" ]; then
|
|
|
|
eerror "$rule: règle invalide: destdir est vide"
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
function __set_dest() {
|
|
|
|
local dest="$1" destdir="$2" filename="$3" force_scp="$4"
|
|
|
|
local userhost remotedir destname
|
|
|
|
if [[ "$dest" == *:* ]]; then
|
|
|
|
splitpair "$dest" userhost remotedir
|
|
|
|
if [ -z "$force_scp" ] && check_userhostname "$userhost"; then
|
|
|
|
# on est déjà avec le bon user sur le bon hôte
|
|
|
|
if [ -n "$remotedir" ]; then
|
|
|
|
splitpath "$remotedir" destdir destname
|
|
|
|
setx destdir=abspath "$destdir" "$HOME"
|
|
|
|
[ -n "$destname" ] || destname="$filename"
|
|
|
|
setx dest=joinp "$destdir" "$destname"
|
|
|
|
else
|
|
|
|
setx dest=joinp "$HOME" "$filename"
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
elif [[ "$dest" == */* ]]; then
|
|
|
|
splitpath "$dest" destdir destname
|
|
|
|
[ -n "$destname" ] || destname="$filename"
|
|
|
|
setx dest=joinp "$destdir" "$destname"
|
|
|
|
setx dest=abspath "$dest"
|
|
|
|
else
|
|
|
|
__check_destdir "$destdir" "$rule" || return 1
|
2017-10-17 22:45:47 +04:00
|
|
|
setx dest=joinp "$destdir" "$dest"
|
2017-04-25 08:27:09 +04:00
|
|
|
fi
|
|
|
|
upvar dest "$dest"
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2017-04-20 23:20:18 +04:00
|
|
|
args=(%
|
|
|
|
--help '$exit_with display_help'
|
2017-04-22 09:49:25 +04:00
|
|
|
-c:,--config: config=
|
2017-12-17 11:46:58 +04:00
|
|
|
-C,--other-configs other_configs=1
|
2017-04-25 08:27:09 +04:00
|
|
|
--file action=file
|
2017-07-19 23:12:06 +04:00
|
|
|
--force-cp force_cp=1
|
2017-04-22 10:08:13 +04:00
|
|
|
-S:,--ssh: SSH=
|
2017-04-25 08:27:09 +04:00
|
|
|
--force-scp force_scp=1
|
2017-10-28 11:25:11 +04:00
|
|
|
-f,--force force=1
|
2017-09-22 21:14:16 +04:00
|
|
|
-r,--recursive recursive=1
|
2017-04-20 23:20:18 +04:00
|
|
|
-n,--fake fake=1
|
2017-04-21 10:12:25 +04:00
|
|
|
-l,--list action=list
|
|
|
|
-e,--edit action=edit
|
2017-04-20 23:20:18 +04:00
|
|
|
)
|
|
|
|
parse_args "$@"; set -- "${args[@]}"
|
|
|
|
|
2017-04-21 10:12:25 +04:00
|
|
|
[ -n "$action" ] || action=file
|
|
|
|
|
2017-04-22 09:49:25 +04:00
|
|
|
## charger toutes les règles
|
|
|
|
|
2017-09-14 22:21:53 +04:00
|
|
|
conf_init -a RULES
|
2017-04-22 09:49:25 +04:00
|
|
|
if [ -n "$config" ]; then
|
|
|
|
if [ "$action" != edit ]; then
|
|
|
|
# le fichier doit exister, sauf en mode édition où il sera créé s'il
|
|
|
|
# n'existe pas déjà
|
|
|
|
[ -f "$config" ] || die "$config: fichier introuvable"
|
|
|
|
fi
|
|
|
|
if [ -f "$config" ]; then
|
|
|
|
source "$config" || die "$config: erreur lors de la lecture du fichier"
|
|
|
|
fi
|
2017-12-17 11:46:58 +04:00
|
|
|
[ -n "$other_configs" ] && conf_load "$HOME/etc/ufile.d/*.conf"
|
2017-04-22 09:49:25 +04:00
|
|
|
else
|
|
|
|
set_defaults ufile
|
2017-12-17 11:46:58 +04:00
|
|
|
conf_load "$HOME/etc/ufile.d/*.conf"
|
2017-04-22 09:49:25 +04:00
|
|
|
fi
|
|
|
|
|
|
|
|
## actions particulières
|
|
|
|
|
2017-04-21 10:12:25 +04:00
|
|
|
if [ "$action" == list ]; then
|
|
|
|
array_to_lines RULES
|
|
|
|
exit 0
|
|
|
|
elif [ "$action" == edit ]; then
|
2017-04-22 09:49:25 +04:00
|
|
|
[ -n "$config" ] || setx config=get_user_defaults_file ufile
|
|
|
|
if [ ! -f "$config" ]; then
|
|
|
|
einfo "Le fichier $(ppath "$config") n'existe pas. Il sera créé avec un contenu par défaut"
|
|
|
|
mkdirof "$config" || die
|
|
|
|
cp "$scriptdir/lib/default/ufile" "$config"
|
2017-04-21 10:12:25 +04:00
|
|
|
fi
|
2017-04-22 09:49:25 +04:00
|
|
|
"${EDITOR:-vi}" "$config"
|
2017-04-21 10:12:25 +04:00
|
|
|
exit $?
|
2017-04-22 10:08:13 +04:00
|
|
|
elif [ "$action" != file ]; then
|
2017-04-22 09:49:25 +04:00
|
|
|
die "bug: $action: action non implémentée"
|
2017-04-21 10:12:25 +04:00
|
|
|
fi
|
|
|
|
|
2017-04-22 09:49:25 +04:00
|
|
|
## classement des fichiers
|
2017-04-21 10:12:25 +04:00
|
|
|
|
2017-04-20 23:20:18 +04:00
|
|
|
if [ -n "$fake" ]; then
|
|
|
|
function docmd() { qvals "$@"; }
|
|
|
|
else
|
|
|
|
function docmd() { "$@"; }
|
|
|
|
fi
|
|
|
|
|
|
|
|
[ $# -gt 0 ] || die "Vous devez spécifier des fichiers à classer"
|
2017-09-14 22:21:53 +04:00
|
|
|
[ ${#RULES[*]} -gt 0 ] || die "Vous devez spécifier des règles pour le classement des fichiers dans ~/etc/default/ufile ou ~/etc/ufile.d/*.conf"
|
2017-04-22 09:49:25 +04:00
|
|
|
|
|
|
|
# vérifier les règles
|
|
|
|
for rule in "${RULES[@]}"; do
|
|
|
|
splitpair "$rule" pattern r2
|
|
|
|
splitpair "$r2" destdir r3
|
|
|
|
splitpair "$r3" renamef r4
|
|
|
|
if [ -z "$destdir" -o "${destdir#"~/"}" != "$destdir" ]; then
|
|
|
|
:
|
|
|
|
elif [ "${destdir#/}" == "$destdir" ]; then
|
|
|
|
ewarn "$rule: règle potentiellement problématique: destdir devrait être absolu"
|
|
|
|
fi
|
|
|
|
done
|
2017-04-20 23:20:18 +04:00
|
|
|
|
2017-09-22 21:14:16 +04:00
|
|
|
# faire la liste des fichiers
|
|
|
|
files=()
|
2017-04-20 23:20:18 +04:00
|
|
|
for file in "$@"; do
|
2017-09-22 21:14:16 +04:00
|
|
|
if [ -d "$file" -a -n "$recursive" ]; then
|
|
|
|
setx file=abspath "$file"
|
|
|
|
array_from_lines rfiles "$(find "$file" -type f)"
|
|
|
|
array_extendu files rfiles
|
|
|
|
elif [ -f "$file" ]; then
|
|
|
|
setx file=abspath "$file"
|
|
|
|
array_addu files "$file"
|
|
|
|
elif [ -n "$fake" ]; then
|
|
|
|
: # on est en mode fake, pas grave si le fichier n'est pas trouvé
|
|
|
|
elif [ -d "$file" ]; then
|
|
|
|
eerror "$file: est un répertoire. essayez avec -r"
|
|
|
|
else
|
2017-04-20 23:20:18 +04:00
|
|
|
eerror "$file: fichier introuvable. il sera ignoré"
|
2017-09-22 21:14:16 +04:00
|
|
|
fi
|
|
|
|
done
|
|
|
|
|
|
|
|
# faire le classement effectif
|
|
|
|
r=
|
|
|
|
for file in "${files[@]}"; do
|
2017-04-20 23:20:18 +04:00
|
|
|
setx pf=abspath "$file"
|
2017-04-22 09:49:25 +04:00
|
|
|
setx dir=dirname -- "$pf"
|
|
|
|
setx filename=basename -- "$pf"
|
2017-04-20 23:20:18 +04:00
|
|
|
found=
|
|
|
|
for rule in "${RULES[@]}"; do
|
|
|
|
splitpair "$rule" pattern r2
|
2017-04-25 08:27:09 +04:00
|
|
|
splitpair "$r2" odestdir r3
|
2017-04-20 23:20:18 +04:00
|
|
|
splitpair "$r3" renamef r4
|
2017-04-25 08:27:09 +04:00
|
|
|
if [ "${odestdir#"~/"}" != "$odestdir" ]; then
|
|
|
|
odestdir="$HOME/${odestdir#"~/"}"
|
|
|
|
elif [ "$odestdir" == "~" ]; then
|
|
|
|
odestdir="$HOME"
|
2017-04-20 23:20:18 +04:00
|
|
|
fi
|
2017-04-22 09:49:25 +04:00
|
|
|
|
2017-10-17 22:45:47 +04:00
|
|
|
if [ "${pattern#/}" != "$pattern" ]; then
|
|
|
|
awk -v filename="$filename" -v pattern="${pattern#/}" 'BEGIN { exit(filename ~ pattern? 0: 1) }' || continue
|
|
|
|
else
|
|
|
|
eval "[[ \"\$filename\" == $(qwc "$pattern") ]]" || continue
|
|
|
|
fi
|
2017-04-22 09:49:25 +04:00
|
|
|
|
2017-04-25 08:27:09 +04:00
|
|
|
unset dest
|
2017-04-22 10:22:18 +04:00
|
|
|
interaction=--DEFAULT--
|
2017-04-22 09:49:25 +04:00
|
|
|
if [ -n "$renamef" ]; then
|
2017-04-25 08:27:09 +04:00
|
|
|
"$renamef" "$filename" "$pf" "$odestdir" || continue
|
2017-04-22 09:49:25 +04:00
|
|
|
fi
|
2017-04-25 08:27:09 +04:00
|
|
|
if is_array dest; then
|
|
|
|
array_copy tmpdests dest
|
|
|
|
dests=()
|
|
|
|
for dest in "${tmpdests[@]}"; do
|
|
|
|
__set_dest "$dest" "$odestdir" "$filename" "$force_scp" || break
|
|
|
|
array_add dests "$dest"
|
|
|
|
done
|
|
|
|
elif is_defined dest; then
|
|
|
|
__set_dest "$dest" "$odestdir" "$filename" "$force_scp" || break
|
|
|
|
dests=("$dest")
|
|
|
|
else
|
|
|
|
__check_destdir "$odestdir" "$rule" || break
|
|
|
|
setx dest=joinp "$odestdir" "$filename"
|
|
|
|
dests=("$dest")
|
2017-04-20 23:20:18 +04:00
|
|
|
fi
|
2017-04-22 09:49:25 +04:00
|
|
|
|
2017-04-25 08:27:09 +04:00
|
|
|
i=1
|
|
|
|
mvi=${#dests[*]}
|
2017-10-28 11:25:11 +04:00
|
|
|
[ -z "$force" ] && mvint=-i || mvint=
|
2017-04-25 08:27:09 +04:00
|
|
|
for dest in "${dests[@]}"; do
|
|
|
|
if [[ "$dest" == *:* ]]; then
|
|
|
|
[ "$interaction" == --DEFAULT-- ] && int= || int="$interaction"
|
|
|
|
estep "$filename --> $dest"
|
|
|
|
ask_yesno $int "Voulez-vous continuer?" O || {
|
|
|
|
r=1; found=x; break
|
|
|
|
}
|
|
|
|
docmd scp ${SSH:+-S "$SSH"} "$file" "$dest" || die "problème lors de la copie du fichier"
|
2017-04-22 10:08:13 +04:00
|
|
|
|
2017-04-25 08:27:09 +04:00
|
|
|
else
|
|
|
|
[ "$interaction" == --DEFAULT-- ] && int=-i || int="$interaction"
|
|
|
|
estep "$filename --> $dest"
|
|
|
|
ask_yesno $int "Voulez-vous continuer?" O || {
|
|
|
|
r=1; found=x; break
|
|
|
|
}
|
|
|
|
setx destdir=dirname -- "$dest"
|
|
|
|
docmd mkdir -p "$destdir" || die "$destdir: impossible de créer le répertoire"
|
2017-07-19 23:12:06 +04:00
|
|
|
if [ $i -eq $mvi -a -z "$force_cp" ]; then
|
2017-04-25 08:27:09 +04:00
|
|
|
mvdesc="du déplacement"
|
|
|
|
mvcmd=mv
|
|
|
|
else
|
|
|
|
mvdesc="de la copie"
|
|
|
|
mvcmd=cp
|
|
|
|
fi
|
2017-10-28 11:25:11 +04:00
|
|
|
docmd "$mvcmd" $mvint "$file" "$dest" || die "problème lors $mvdesc du fichier"
|
2017-04-25 08:27:09 +04:00
|
|
|
fi
|
|
|
|
i=$(($i + 1))
|
|
|
|
done
|
2017-04-22 10:08:13 +04:00
|
|
|
|
2017-04-25 08:27:09 +04:00
|
|
|
[ -n "$found" ] || found=1
|
2017-04-22 09:49:25 +04:00
|
|
|
break
|
2017-04-20 23:20:18 +04:00
|
|
|
done
|
2017-04-25 08:27:09 +04:00
|
|
|
|
2017-04-20 23:20:18 +04:00
|
|
|
if [ -z "$found" ]; then
|
|
|
|
ewarn "$file: aucune correspondance n'a été trouvée"
|
|
|
|
fi
|
|
|
|
done
|
2017-04-25 08:27:09 +04:00
|
|
|
|
|
|
|
[ -n "$r" ] || r=0
|
|
|
|
exit $r
|