#!/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 urequire DEFAULTS crontab install function display_help() { uecho "$scriptname: lancer une suite de commande en respectant une planification de type cron USAGE $scriptname [options] [/path/to/uscrontab] [var=value...] $scriptname -e [/path/to/uscrontab] $scriptname -l La première forme du script doit normalement être lancé toutes les minutes par une tâche cron. Utiliser l'option --install pour ajouter automatique la ligne dans la crontab de l'utilisateur. Avec la première forme du script, le fichier spécifié est traité. Si aucun fichier n'est spécifié, fusionner s'il existe le fichier $USCRONTAB_USERFILE avec chacun des fichiers du répertoire $USCRONTAB_USERDIR dans un fichier temporaire, puis exécuter le fichier résultat avec le nom virtuel $USCRONTAB_USER note: le nom virtuel est utilisé pour le verrouillage avec --lock A chaque lancement de ce script, le fichier /path/to/uscrontab spécifié est examiné pour déterminer quels commandes doivent être exécutées. Ce fichier est composé de lignes dans un format particulier, qui sont analysées et traitées dans l'ordre. Quelles que soient les lignes qui sont sélectionnées pour l'exécution, elles sont garanties de s'exécuter dans l'ordre du fichier, l'une après l'autre. Les définitions var=value mentionnées sur la ligne de commande sont des définitions de variables à effectuer avant de lancer les commandes. Les lignes commençant par # sont des commentaires et sont ignorées == Définitions de variables et exécution de commandes == Les lignes de la forme suivante sont des définitions de variable: [export] var=\"valeur de la variable\" Ces lignes sont des définitions de variable bash qui sont exécutées telles quelles. Il n'est donc pas autorisé de mettre des espaces autour de =. Par exemple, les lignes suivantes sont des erreurs de syntaxe: var = bad var=pas de quotes autour de la valeur alors que celles-ci sont correctes: var=ok var=\"valeur avec des espaces\" var='on peut utiliser des quotes aussi' Il est possible de manipuler les variables de type PATH avec une syntaxe particulière de l'opérateur d'assignation. Les opérateurs += et %= utilisent uaddpath(), #= utilise uinspath() et -= utilise udelpath(). Par exemple, les lignes suivantes ajoutent respectivement /usr/local/nutools puis enlèvent /opt/rogue au PATH: PATH+=/usr/local/nutools PATH-=/opt/rogue Bien sûr, il ne faut pas oublier de quoter les espaces: PATH+=\"/path/to/dir with spaces\" La syntaxe ?= permet de définir la valeur d'une variable si elle n'est pas déjà définie: var?=default Les lignes de la forme suivante permettent d'exécuter une commande qui est exécutée systématiquement et ignore la planification: \$one-line-command Une variante permet de spécifier des commandes sur plusieurs lignes. ATTENTION! \${ et $} doivent être tous seuls sur la ligne. \${ several commands ... $} Ces commandes sont exécutées systématiquement et ignorent la planification. On peut s'en servir notamment pour lire un fichier de configuration qui définit des variables ou des fonctions: \$source path/to/file Le code d'erreur de ces commandes est ignoré, contrairement à ce qui se passe pour les commandes qui font l'objet d'une planification. == Planification de commandes == Les autres lignes doivent être au format d'une ligne de crontab: minutes hours days months dows command-line command-line peut être n'importe quelle ligne de commande bash, pourvu qu'elle soit sur une seule ligne. Certaines extensions par rapport à la syntaxe de crontab sont autorisées. Il est en particulier possible de spécifier plusieurs planifications pour une seule commande. Par exemple, les lignes suivantes permettent d'exécuter 'command' toutes les heures ET à 1h05: 0 * * * * 5 1 * * * command Il est aussi possible d'utiliser la même planification pour plusieurs commandes sans devoir répéter la définition de la planification. Les lignes suivantes planifient command1 et command2 toutes les heures: 0 * * * * command1 command2 Pour être prise en compte, la ligne command2 doit commencer par au moins un espace ou une tabulation. Pour la lisibilité, la syntaxe suivante est supportée aussi: 0 * * * * command1 command2 Les deux formats peuvent être utilisés ensemble. Par exemple les lignes suivantes exécutent command1 et command2 toutes les heures ET à 1h05: 0 * * * * 5 1 * * * command1 command2 Par défaut, le script s'arrête à la première commande planifiée qui retourne avec un code d'erreur. Il est possible d'ignorer le code d'erreur d'une commande avec nostop, e.g: 0 * * * * nostop command Cf aussi l'option --continuous pour modifier le comportement par défaut == Fonctions disponibles == La fonction check_pidfile() est disponible, et permet de vérifier qu'une opération n'est pas déjà en cours. Si cette fonction est utilisée, il ne faut pas modifier la valeur de -k. Par exemple: 0 1 * * * check_pidfile /path/to/pid [args] long-running-script check_pidfile() doit être utilisée toute seule sur la ligne et s'utilise avec les argument suivants: check_pidfile PIDFILE [DESC] [BARRIER] - PIDFILE est le fichier de PID qui est vérifié - DESC est la description du traitement qui est effectué. La valeur par défaut est \"Une synchronisation\". Si le fichier de PID est présent, le message suivant est affiché: DESC est en cours. Si vous pensez que c'est une erreur, veuillez vérifier le process de pid PID puis supprimez le cas échéant le fichier PIDFILE - BARRIER est un fichier qui est créé avec le contenu 'PID' s'il n'existe pas encore, et si la vérification du fichier de PID est faite avec succès. La présence de ce fichier peut-être vérifiée par un processus externe pour empêcher par exemple de mettre à jour les scripts pendant qu'il sont en train de tourner. Son contenu peut être examiné pour connaître le PID du processus qui l'a créé initialement. Le fichier est automatiquement supprimé à la fin de ce script. Attention: ce fichier n'est pas un verrou, il peut être supprimé à tout moment. Notamment, si deux scripts sont configurés pour créer le même fichier barrière, le premier script supprimera le fichier barrière avant la fin de l'exécution du second script. La fonction remove_pidfile() permet de supprimer un fichier de pid pour spécifier qu'une opération est terminée. Considérons l'exemple suivant: 0 1 * * * check_pidfile /path/to/pid script1 script2 remove_pidfile /path/to/pid script3 Dans cet exemple, il ne faut pas qu'une autre occurence de script1 tourne pendant que script2 tourne. Par contre, plusieurs occurences de script3 peuvent tourner en parallèle. La fonction elogto() permet de spécifier un fichier vers lequel toutes les sorties sont redirigées. OPTIONS -A, --install Installer une planification toutes les minutes du script dans la crontab de l'utilisateur. Si l'argument /path/to/uscrontab n'est pas spécifié, c'est une planification générique qui exécute les fichiers par défaut qui est installée. -R, --uninstall Désinstaller la planification toutes les minutes du script de la crontab de l'utilisateur. Si l'argument /path/to/uscrontab est spécifié, cette instance est désinstallée. Sinon, ne désinstaller que la planification générique. -d, --disable-only Avec l'option -R, désactiver la planification au lieu de la supprimer. -e, --edit Lancer un editeur pour modifier l'uscrontab spécifiée. Si aucun fichier n'est spécifié, éditer $USCRONTAB_USERFILE -a, --add Installer un script uscrontab dans le répertoire approprié. L'argument doit être de la forme [name:]/path/to/uscrontab Si name n'est pas spécifié, le nom de base du fichier spécifié est utilisé. Si name est vide ou vaut \$USER (soit $USER en l'occurence), copier le fichier spécifié vers le chemin $USCRONTAB_USERFILE Sinon, copier le fichier spécifié vers $USCRONTAB_USERDIR/name -r, --remove Supprimer le script uscrontab spécifié. L'argument doit être le nom du script à supprimer. Si l'argument n'est pas spécifié ou vaut \$USER (soit $USER en l'occurence), supprimer le fichier $USCRONTAB_USERFILE s'il existe -l, --list Si l'argument /path/to/crontab est spécifié, afficher le contenu de ce fichier. Sinon, lister les contenus des fichiers crontab qui sont exécutés avec la planification actuelle. Si une planification générique est installée, ou si aucune planification n'est en cours, afficher le contenu du fichier $USCRONTAB_USERFILE et chacun des fichiers du répertoire $USCRONTAB_USERDIR -N, --nop Ne pas examiner la planification. Traiter uniquement l'option --osvar le cas échéant. -n, --fake Afficher au lieu de les exécuter les commandes qui doivent être lancées -P, --pause-for NBMINS Désactiver les planifications pendant NBMINS minutes. Utiliser -1 pour désactiver les planifications sans limite de durée. Pendant la période de pause, toutes les invocations de uscrontab n'ont aucun effet, sauf si on utilise l'option --force -Y, --unpause Réactiver les planifications après une mise en pause -p, --pause Désactiver les planifications pendant 1 journée. Equivalent à -P 1440 -f, --force Forcer l'exécution de la planification, même si elle a été mise en pause avec l'option --pause -v, --osvar NAME[=VALUE] Définir une variable de type oneshot. Par défaut, VALUE vaut 1 si elle n'est pas spécifiée. OPTIONS AVANCEES --lock LOCKFILE Inscrire dans le fichier spécifié des informations permettant d'éviter les invocations simultanées de ce script. Si selon ce fichier, le script tourne depuis plus de $USCRONTAB_LOCKDELAY heures, un message d'erreur est consigné et un message d'avertissement est affiché au plus une fois. Utiliser --lock '' pour désactiver cette fonctionnalité Par défaut, si ce script est lancé en root, le fichier utilisé pour le verrouillage est de la forme /var/run/$scriptname/abspath/to/crontab Si le script est lancé avec un compte utilisateur, aucun verrouillage n'est effectué. --lockdelay LOCKDELAY[=$USCRONTAB_LOCKDELAY] Changer le nombre d'heures pendant lesquelles on autorise le script a verrouiller l'exécution avant d'afficher un avertissement. -c, --continuous Par défaut, ce script s'arrête à la première commande planifiée qui retourne avec un code d'erreur. Notez que les codes d'erreur des commandes sans planification sont toujours ignorés. Avec cette option, ce script ne s'arrête jamais, bien qu'il retourne toujours un code d'erreur si une erreur s'est produite. Il est possible d'ignorer le code d'erreur pour une commande en particulier avec le préfixe nostop -k, --stopec EXITCODE[=101] Spécifier un code d'erreur spécial qui arrête ce script sans erreur, ou '' pour désactiver cette fonctionnalité. Ceci permet en début de script de faire des tests par exemple sur l'environnement avant de lancer les scripts planifiés. Si l'environnement ne convient pas, il suffit au script de contrôle de retourner le code d'erreur spécifique pour arrêter le traitement. --show-ctnow Afficher l'heure de référence au format crontab 'min hou day mon dow' Cette valeur peut être utilisée avec l'option --force-ctnow dans des tests pour reproduire une condition spécifique. --force-ctnow 'min hou day mon dow' Pour le développement ou des tests, forcer la valeur de l'heure de référence. Il faut respecter le format, sinon les résultats ne sont pas garantis. Le mieux est de reprendre le résultat de l'option --show-ctnow en le modifiant un peu si nécessaire. -@, --hm H[:M] -H, --h00 -M, --h30 Options pour simplifier l'utilisation de --force-ctnow. Supposons que --show-ctnow affiche 'Y X day mon dow', alors: -@ H:M | est équivalent à | --force-ctnow 'M H day mon dow' -H | '' | --force-ctnow '0 X day mon dow' -M | '' | --force-ctnow '30 X day mon dow' -G, --any-ctnow Pour le développement ou des tests, lancer toutes les commandes dans l'ordre sans tenir compte de l'heure de référence. Cette commande ne devrait pas être utilisée en temps normal, mais elle existe pour simplifier les tests avec --show-ctnow + --force-ctnow dans les cas simples." } function set_usercrontabs() { # initialiser le tableau $1(=usercrontabs) avec la liste suivante: le # fichier $USCRONTAB_USERFILE s'il existe, puis la liste des fichiers dans # le répertoire $USCRONTAB_USERDIR local -a _userfile _userdir [ -f "$USCRONTAB_USERFILE" ] && _userfile=("$USCRONTAB_USERFILE") array_lsfiles _userdir "$USCRONTAB_USERDIR" eval "${1:-usercrontabs}"'=("${_userfile[@]}" "${_userdir[@]}")' } function get_usercrontab() { # trouver le chemin correspondant au crontab $1. Si $1 n'est pas spécifié ou # vaut $USER, afficher $USCRONTAB_USERFILE, sinon afficher $USCRONTAB_USERDIR/$1 local name="$1" [ "$name" == "$USER" ] && name= if [ -n "$name" ]; then echo "$USCRONTAB_USERDIR/$name" else echo "$USCRONTAB_USERFILE" fi } function check_pause() { # retourner 0 si on est en pause local pauseuntil pausefile="$pausefile" now="$2" if [ -f "$pausefile" ]; then pauseuntil="$(<"$pausefile")" if [ -z "$pauseuntil" ]; then # pause sans limitation de durée return 0 elif [ "$pauseuntil" -gt "$now" ]; then return 0 fi fi return 1 } USCRONTAB_CTLINE="* * * * * $script" USCRONTAB_LOCKDELAY=8 USCRONTAB_STOPEC=101 USCRONTAB_BASEDIR=/var/local/uscrontab USCRONTAB_USERFILE="$USCRONTAB_BASEDIR/users/$USER" USCRONTAB_USERDIR="$USCRONTAB_BASEDIR/users.d/$USER" USCRONTAB_USER="$USCRONTAB_BASEDIR/$USER" action=run disable_only= lockfile=auto lockdelay= fake= continuous= force_ctnow= pause= force= osvars=() parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ -A,--add,--install action=install \ -R,--remove,--uninstall action=uninstall \ -d,--disable-only '$action=uninstall; disable_only=1' \ -e,--edit action=edit \ -a,--add action=add \ -r,--remove action=remove \ -l,--list action=list \ -N,--nop action=nop \ -n,--fake fake=1 \ -P:,--pause-for: '$action=pause; set@ pause' \ -p,--pause action=pause \ -Y,--unpause action=unpause \ -f,--force force=1 \ -v:,--osvar: osvars \ --lock: lockfile= \ --lockdelay: lockdelay= \ -c,--continuous continuous=1 \ -k:,--stop: USCRONTAB_STOPEC= \ --show-ctnow action=show-ctnow \ --force-ctnow: __CTRESOLVE_CTNOW= \ -@:,--hm: force_ctnow= \ -H,--h00 force_ctnow=h00 \ -M,--h30 force_ctnow=h30 \ -G,--any-ctnow __CTRESOLVE_CTNOW="**ANY**" \ @ args -- "$@" && set -- "${args[@]}" || die "$args" pausefile="$USCRONTAB_USERFILE.pauseuntil" now="$(awk 'BEGIN { print int(systime() / 60) * 60 }')" # ne prendre le premier argument comme un fichier que s'il existe ou s'il n'est # pas de la forme *=* if [[ "$1" == *=* ]]; then if [ -f "$1" ]; then uscrontab="$1"; shift fi else uscrontab="$1"; shift fi # écrire les variables oneshot for osvar in "${osvars[@]}"; do if [[ "$osvar" == *=* ]]; then splitvar "$osvar" name value else name="$osvar" value=1 fi name="${name##*/}" # au cas où... echo -n "$value" >"$USCRONTAB_USERFILE.$name.osvar" done if [ "$action" == "pause" ]; then [ -n "$pause" ] || pause=1440 if [ "$pause" -lt 0 ]; then pauseuntil= else pauseuntil=$(($now + $pause * 60)) fi echo "$pauseuntil" >"$pausefile" || die enote "Désactivation des planifications pour $pause minute(s)" exit 0 elif [ "$action" == "unpause" ]; then if [ -f "$pausefile" ]; then rm "$pausefile" || die enote "Réactivation des planifications" fi exit 0 elif [ "$action" == "edit" ]; then if [ -z "$uscrontab" ]; then basedir="$(dirname "$USCRONTAB_USERFILE")" [ -d "$basedir" ] || die "$basedir: ce répertoire n'existe pas. Vérifiez l'installation de nutools" uscrontab="$USCRONTAB_USERFILE" fi enote "Edition de $uscrontab" if [ ! -f "$uscrontab" ]; then touch "$uscrontab" || die chmod 640 "$uscrontab" fi "${EDITOR:-vi}" "$uscrontab" || die exit 0 elif [ "$action" == "add" ]; then [ -n "$uscrontab" ] || die "Vous devez spécifier le fichier à installer" if [[ "$uscrontab" == *:* ]]; then splitfsep2 "$uscrontab" : name uscrontab [ -f "$uscrontab" ] || die "$uscrontab: fichier introuvable" else name="$(basename "$uscrontab")" fi dest="$(get_usercrontab "$name")" destdir="$(dirname -- "$dest")" if [ ! -d "$destdir" ]; then mkdir -p "$destdir" || die chmod 750 "$destdir" fi copy_update_ask "$uscrontab" "$dest" exit 0 elif [ "$action" == "remove" ]; then uscrontab="$(get_usercrontab "$uscrontab")" if [ -f "$uscrontab" ]; then ask_yesno "Voulez-vous supprimer le fichier $uscrontab?" C || die enote "Suppression de $uscrontab" rm "$uscrontab" || die fi exit 0 elif [ "$action" == "list" ]; then if [ -n "$uscrontab" ]; then uscrontab="$(abspath "$uscrontab")" array_from_lines ctfiles "$(crontab -l 2>/dev/null | awkrun script="$script" uscrontab="$uscrontab" '$6 == script && $7 == uscrontab { print $7 }')" if [ ${#ctfiles[*]} -eq 0 ]; then ewarn "$(ppath "$uscrontab"): non planifié" ctfiles=("$uscrontab") fi else array_from_lines ctfiles "$(crontab -l 2>/dev/null | awkrun script="$script" '$6 == script { if ($7) print $7; else if ($0 ~ /^#/) print "#GENERIC"; else print "GENERIC" }')" #' if array_contains ctfiles "GENERIC"; then # il y a une planification générique array_del ctfiles "GENERIC" set_usercrontabs usercrontabs array_extend ctfiles usercrontabs elif array_contains ctfiles "#GENERIC"; then # il y a une planification générique désactivée array_del ctfiles "#GENERIC" set_usercrontabs usercrontabs array_extend ctfiles usercrontabs ewarn "Planification générique désactivée. Vous pouvez la réactiver avec $scriptname -A" elif [ ${#ctfiles[*]} -eq 0 ]; then einfo "Aucune planification en cours" set_usercrontabs ctfiles fi fi r=1 for ctfile in "${ctfiles[@]}"; do r=0 # il y a au moins une planification etitle "$(ppath "$ctfile")" \ cat "$ctfile" done if check_pause "$pausefile" "$now"; then ewarn "En pause. Réactiver avec $scriptname -Y" fi exit $r elif [ "$action" == "show-ctnow" ]; then ctnow "$now" exit 0 elif [ "$action" == "nop" ]; then exit 0 fi [ -z "$uscrontab" -o -f "$uscrontab" ] || die "$uscrontab: fichier introuvable" [ -n "$uscrontab" ] && uscrontab="$(abspath "$uscrontab")" if [ "$action" == "install" ]; then ctline="$USCRONTAB_CTLINE" [ -n "$uscrontab" ] && ctline="$ctline $(qvalm "$uscrontab")" enable_in_crontab "$ctline" && estep "add_to_crontab $ctline" elif [ "$action" == "uninstall" ]; then ctline="$USCRONTAB_CTLINE" [ -n "$uscrontab" ] && ctline="$ctline $(qvalm "$uscrontab")" if [ -n "$disable_only" ]; then disable_in_crontab "$ctline" && estep "disable_in_crontab $ctline" else remove_from_crontab "$ctline" && estep "remove_from_crontab $ctline" fi elif [ "$action" == "run" ]; then if [ -n "$force" ]; then # ne pas tenir compte de l'état de pause : elif check_pause "$pausefile" "$now"; then isatty && ewarn "En pause. Réactiver avec $scriptname -Y" ac_clean "$lockfile" exit 0 elif [ -f "$pausefile" ]; then rm "$pausefile" fi if [ -n "$uscrontab" ]; then default_lockfile="/var/run/$scriptname$uscrontab.lock" lockdesc="$scriptname $uscrontab" else set_usercrontabs usercrontabs ac_set_tmpfile uscrontab for usercrontab in "${usercrontabs[@]}"; do echo "# $usercrontab" >>"$uscrontab" cat "$usercrontab" >>"$uscrontab" done default_lockfile="/var/run/$scriptname$USCRONTAB_USER.lock" lockdesc="$scriptname $USCRONTAB_USER" fi if [ "$lockfile" == auto ]; then if is_root; then lockfile="$default_lockfile" mkdirof "$lockfile" || die else lockfile= fi fi [ -n "$lockdelay" ] || lockdelay="$USCRONTAB_LOCKDELAY" if [ -n "$lockfile" ]; then lastmsgfile="${lockfile%.lock}.lastmsg" retry=1 while [ -n "$retry" ]; do case "$(lf_trylock -h "$lockdelay" "$lockfile")" in locked) edebug "Arrêt du script parce que le verrou $lockfile est posé" exit 0 ;; stale) msg="Un verrou sur '$lockdesc' est posé depuis plus de $lockdelay heures. Veuillez faire vos vérification et supprimer le cas échéant le fichier $lockfile" lastmsg= [ -f "$lastmsgfile" ] && lastmsg="$(<"$lastmsgfile")" || lastmsg= logger -p cron.warn -t "$scriptname" -- "$msg" if [ "$msg" != "$lastmsg" ]; then ewarn "$msg" echo "$msg" >"$lastmsgfile" else edebug "$msg" fi exit 1 ;; retry) :;; *) retry=;; esac done rm -f "$lastmsgfile" 2>/dev/null autoclean "$lockfile" fi function __should_fork() { # tester si la commande doit être lancée dans un sous-shell local cmd cmd="${1%% *}" case "$cmd" in check_pidfile|remove_pidfile|osvar|elogto) return 1;; esac return 0 } function __ctexec() { local ec=0 local cmd="${1%% *}" if [ "$cmd" == nostop ]; then cmd="${1#nostop }" shift set -- "$cmd" "$@" local continuous=1 fi if [ -n "$fake" ]; then echo "$*" else edebug "$*" if __should_fork "$1"; then (eval "$*"); ec=$? else # cas particulier: certaines fonctions doivent être exécutée # dans le contexte courant, et non pas dans un sous-shell eval "$*"; ec=$? fi fi [ -n "$USCRONTAB_STOPEC" -a "$ec" == "$USCRONTAB_STOPEC" ] && exit 0 [ -z "$continuous" -a "$ec" != 0 ] && exit "$ec" return 0 } function __cterror() { die "$*" } if [ -n "$force_ctnow" ]; then ctnow=($(ctnow "$now")) case "$force_ctnow" in h00) ctnow[0]=0;; h30) ctnow[0]=30;; *) force_ctnow="${force_ctnow//./:}" # autoriser H.M en plus de H:M splitpair "$force_ctnow" h m ctnow[0]="${m:-0}" ctnow[1]="$h" ;; esac __CTRESOLVE_CTNOW="${ctnow[*]}" fi __ctscript="$(ctresolve <"$uscrontab")" ec=0 edebug "$__ctscript" ( # tableau des fichiers de pid en cours. la conséquence est que ce n'est # pas une erreur d'appeler à plusieurs reprises check_pidfile avec le # même fichier __USCRONTAB_PIDFILES=() function check_pidfile() { if [ -n "$1" ]; then local pidfile="$(abspath "$1")" if ! array_contains __USCRONTAB_PIDFILES "$pidfile"; then local status pidfile_set -r "$pidfile"; status=$? case "$status" in 1) eerror "${2:-Une synchronisation} est en cours. Si vous pensez que c'est une erreur, veuillez vérifier le process de pid $(<"$pidfile") puis supprimez le cas échéant le fichier $pidfile" return "$USCRONTAB_STOPEC" ;; 10) die "Une erreur s'est produite pendant l'écriture du fichier de pid. Impossible de continuer" ;; esac array_add __USCRONTAB_PIDFILES "$pidfile" fi fi if [ -n "$3" -a -w "$(dirname "$3")" ]; then (set -o noclobber echo_ $$ >"$3" && chmod 644 "$3" ) 2>/dev/null && autoclean "$3" fi return 0 } function remove_pidfile() { if [ -n "$1" ]; then local pidfile="$(abspath "$1")" ac_clean "$pidfile" array_del __USCRONTAB_PIDFILES "$pidfile" fi } function osvar() { local __name __value for __name in "$@"; do splitvar "$__name" __name __value if [ -f "$USCRONTAB_USERFILE.$__name.osvar" ]; then __value="$(<"$USCRONTAB_USERFILE.$__name.osvar")" rm "$USCRONTAB_USERFILE.$__name.osvar" fi _setv "$__name" "$__value" done } __ac_forgetall # Si des variables sont spécifiées, les initialiser avant de lancer le # script for __var in "$@"; do splitvar "$__var" __name __value edebug "$__name=$__value" _setv "$__name" "$__value" done eval "$__ctscript" ac_cleanall ); ec=$? ac_clean "$uscrontab" ac_clean "$lockfile" exit "$ec" fi