diff --git a/CHANGES.md b/CHANGES.md index 68fc0bb..2073d81 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,28 @@ +## Version 7.2.0 du 26/09/2017-17:01 + +* `1e576da` multiconf: rajouter conf_upgrade() dans conf_auto() +* `eeef48d` Intégration de la branche update-config + * `ea45055` implémentation simplifiée avec conf_upgrade() + * `efc60b3` description des modifications +* `36eec4a` Intégration de la branche update-ufile + * `2afffef` ne pas implémenter la recherche automatique de fichier de configuration + * `6ec37f6` ufile: implémenter l'option -r + * `6e8c200` maj TODO +* `cf629e6` uscrontab: support des oneshot vars +* `a66c0c2` uscrontab: ajout des options -@, -H, -M +* `52e838e` uwatch: ajouter les options -w et -p pour attendre la disparition d'un fichier ou d'un processus +* `8ad2e4c` lftp ne supporte pas l'utilisation des guillemets +* `7fb81a0` désactiver la vérification du certificat par défaut +* `dbc3daa` multiconf: bug +* `aff1c15` multiconf: support des variables de type path et compatibilité avec bash 4.3 +* `8e17fb1` Intégration de la branche add-multiconf + * `a916180` ajouter conf_install pour installer les fichiers de configuration dans un répertoire standardisé + * `0372a5c` ufile charge les fichiers de ~/etc/ufile.d/ + * `7d1ec90` simplifier l'API + * `c988692` fin implémentation + * `72e0c36` maj doc + * `f472501` squelette initial + ## Version 7.1.0 du 10/09/2017-19:20 * `f131e21` uwatch: ajout de l'option -r diff --git a/VERSION.txt b/VERSION.txt index a3fcc71..0ee843c 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -7.1.0 +7.2.0 diff --git a/authftp b/authftp index 5696705..d7e8fe6 100755 --- a/authftp +++ b/authftp @@ -28,6 +28,9 @@ OPTIONS --tls Indiquer que la connexion se fera en TLS. Implique --lftp puisque ncftp ne le supporte pas. + --verify-certificate + Avec la connexion --tls, forcer la vérification du certificat, qui est + désactivée par défaut. note: A cause d'une limitation de lftp, ce n'est pas possible de se connecter automatiquement avec lftp si le mot de passe contient une virgule. A cause de la @@ -43,6 +46,7 @@ noproxy="$AUTHFTP_PROXY_DISABLED" lftp= options=() tls= +verify_certificate=no parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ -p,--proxy noproxy= \ @@ -50,6 +54,7 @@ parse_opts "${PRETTYOPTS[@]}" \ -l,--lftp lftp=1 \ -o:,--option: options \ --tls tls=1 \ + --verify-certificate verify_certificate=yes \ @ args -- "$@" && set -- "${args[@]}" || die "$args" [ -n "$noproxy" -o -n "$AUTHFTP_PROXY_HOST" ] || die "AUTHFTP_PROXY_HOST doit être défini" @@ -66,9 +71,13 @@ read_value -i "Entrez le chemin" path "$4" N if [ -n "$lftp" ]; then if [ -n "$noproxy" ]; then - exec lftp -u "$login,$password" "${options[@]}" "ftp://$host/$path" + exec lftp "${options[@]}" -e "\ +set ssl:verify-certificate $verify_certificate +open -u $login,$password ftp://$host/$path" else - exec lftp -u "${login}@${my_login}@${host},${password}@${my_password}" "${options[@]}" "ftp://$AUTHFTP_PROXY_HOST/$path" + exec lftp "${options[@]}" -e "\ +set ssl:verify-certificate $verify_certificate +open -u ${login}@${my_login}@${host},${password}@${my_password} ftp://$AUTHFTP_PROXY_HOST/$path" fi else if [ -n "$noproxy" ]; then diff --git a/lib/default/ufile b/lib/default/ufile index cb065f1..755d24a 100644 --- a/lib/default/ufile +++ b/lib/default/ufile @@ -1,9 +1,5 @@ # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 -# Fichiers externes à inclure. Chacun de ces fichiers peut contenir des -# définitions de fonctions et de la variables RULES -INCLUDES=() - # Règles pour le classement des fichiers. Chaque règle est de la forme # pattern:destdir[:renamef] RULES=() diff --git a/lib/ulib/install b/lib/ulib/install index 89de2fc..014bb33 100644 --- a/lib/ulib/install +++ b/lib/ulib/install @@ -111,7 +111,15 @@ function copy_update_ask() { # Copier ou mettre à jour le fichier $1 vers le fichier $2. # Si le fichier existe déjà, la différence est affichée, et une confirmation # est demandée pour l'écrasement du fichier. + # si $1 commence par '-' alors on s'en sert comme option pour configurer le + # niveau d'interaction pour demander la confirmation. les paramètres sont + # alors décalés # Retourner vrai si le fichier a été copié sans erreur. + local interopt=-c + if [[ "$1" == -* ]]; then + interopt="$1" + shift + fi local src="$1" dest="$2" [ -d "$dest" ] && dest="$dest/$(basename -- "$src")" @@ -119,10 +127,14 @@ function copy_update_ask() { [ -f "$dest" ] || copy_replace "$src" "$dest" if testdiff "$src" "$dest"; then - diff -u "$dest" "$src" - if ask_yesno -c "Voulez-vous remplacer $(ppath "$dest") par la nouvelle version?" C; then + check_interaction "$interopt" && diff -u "$dest" "$src" + if ask_yesno "$interopt" "Voulez-vous remplacer $(ppath "$dest") par la nouvelle version?" C; then copy_replace "$src" "$dest" "$3" return $? + elif ! check_interaction "$interopt"; then + ewarn "Les mises à jours suivantes sont disponibles:" + diff -u "$dest" "$src" + ewarn "Le fichier $(ppath "$dest") n'a pas été mis à jour" fi fi return 1 diff --git a/lib/ulib/multiconf b/lib/ulib/multiconf new file mode 100644 index 0000000..21b381a --- /dev/null +++ b/lib/ulib/multiconf @@ -0,0 +1,307 @@ +##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 +## Gestion de fichiers de configuration et de répertoires de configuration +##@cooked nocomments +uprovide multiconf +urequire install + +function conf_local() { + # afficher les commandes pour définir comme locales les variables utilisées + # par les fonctions conf_* + # cela permet d'utiliser ces fonctions à l'intérieur d'autres fonctions sans + # polluer l'espace de nom + echo "local -a __CONF_DESCS __CONF_ARRAY_VARS __CONF_PATH_VARS" +} + +function conf_auto() { + # charger la configuration pour l'outil $1 avec les variables $2..@ + # conf_init n'est appelé que si des variables sont spécifiées, ce qui permet + # d'appeler conf_init au préalable si une configuration spécifique doit être + # faite. + # Ainsi: + # conf_auto NAME VARS... + # est équivalent à: + # conf_init VARS... + # conf_auto NAME + # est équivalent à: + # conf_init VARS... + # conf_find_files __CONF_FILES ~/etc/default/NAME.conf ~/etc/NAME.d/*.conf + # conf_upgrade ~/etc/default/NAME.conf + # conf_load_files "${__CONF_FILES[@]}" + # Pour supporter les scénarii où les fichiers de configuration sont ailleurs + # que dans ~/etc/default, l'argument NAME peut être un chemin: + # conf_auto PATH/TO/NAME VARS... + # est équivalent à: + # conf_init VARS... + # conf_find_files __CONF_FILES PATH/TO/NAME.conf PATH/TO/NAME.d/*.conf + # conf_upgrade PATH/TO/NAME.conf + # conf_load_files "${__CONF_FILES[@]}" + local __name="$1"; shift + [ -n "$__name" ] || return 1 + [ $# -gt 0 ] && conf_init "$@" + local -a __CONF_FILES + if [[ "$__name" == */* ]]; then + conf_find_files __CONFS_FILES "$__name.conf" "$__name.d/*.conf" + [ $# -gt 0 ] && conf_upgrade "$__name.conf" + else + conf_find_files __CONFS_FILES "$HOME/etc/default/$__name.conf" "$HOME/etc/$__name.d/*.conf" + [ $# -gt 0 ] && conf_upgrade "$HOME/etc/default/$__name.conf" + fi + conf_load_files "${__CONFS_FILES[@]}" +} + +function conf_init() { + # définir les variables attendues lors du chargement des fichiers de + # configuration par conf_load_files() + # Si cette fonction n'a pas d'argument, le contenu du tableau CONFIG s'il + # est existe est utilisé + # par défaut, les variables sont en mode scalaire: la définition d'une + # variable écrase la valeur précédente. Avec l'option -a les variables sont + # en mode tableau: les nouvelles valeurs sont rajoutées à la fin du tableau. + # Avec l'option -p les variables sont en mode chemin: les nouvelles valeurs + # sont ajoutées si elles n'existe pas déjà avec le séparateur ':' + # dans l'exemple suivant: + # conf_init NAME VALUE -a SRCDIRS DESTDIRS -p LIBPATH + # NAME et VALUE sont scalaires, SRCDIRS et DESTDIRS sont des tableaux et + # LIBPATH est de type chemin + # Les variables scalaires et chemin sont initialisées à la valeur vide ou à + # la valeur spécifiée e.g.: + # conf_init VAR=value MYPATH=a:b:c + # Les variables tableaux sont toujours initialisées à la valeur vide + # L'option -s permet de revenir au mode scalaire + + # Note: il est possible d'associer une description à chaque variable ainsi + # qu'un en-tête, ce qui permet de construire le fichier de configuration ou + # de mettre à jour un fichier existant avec conf_upgrade(). Par exemple, les + # commandes suivantes: + # CONFIG=( + # "# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8" + # "# configurer l'application" + # -s + # "NAME=payet//nom de l'administrateur" + # "MAIL=admin@host.tld//mail de contact" + # -a + # "HOSTS//hôtes autorisés à se connecter" + # ) + # conf_init + # permettent de générer automatiquement le fichier de configuration suivant: + # # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 + # # configurer l'application + # + # # nom de l'administrateur + # #NAME=payet + # + # # mail de contact + # #MAIL=admin@host.tld + # + # # hôtes autorisés à se connecter + # #HOSTS=() + __CONF_DESCS=() + __CONF_ARRAY_VARS=() + __CONF_PATH_VARS=() + local __type=scalar __initial=1 __prefix __var __desc + [ $# -eq 0 ] && is_array CONFIG && set -- "${CONFIG[@]}" + while [ $# -gt 0 ]; do + if [ -n "$__initial" ]; then + if [ "${1:0:1}" == "#" ]; then + [ ${#__prefix} -gt 0 ] && __prefix="$__prefix"$'\n' + __prefix="$__prefix$1" + shift + continue + else + [ -n "$__prefix" ] && array_add __CONF_DESCS "$__prefix" + __initial= + fi + fi + case "$1" in + -a|--array) __type=array;; + -p|--path) __type=path;; + -s|--scalar) __type=scalar;; + *) + array_add __CONF_DESCS "$1" + splitfsep "$1" // __var __desc + case "$__type" in + array) + eval "${__var%%=*}=()" + array_addu __CONF_ARRAY_VARS "${__var%%=*}" + array_del __CONF_PATH_VARS "${__var%%=*}" + ;; + path) + setv "$__var" + array_addu __CONF_PATH_VARS "${__var%%=*}" + array_del __CONF_ARRAY_VARS "${__var%%=*}" + ;; + scalar) + setv "$__var" + ;; + esac + ;; + esac + shift + done +} + +function conf_load() { + # charger les fichiers de configuration spécifiés + # conf_load SPECS... + # est équivalent à: + # conf_find_files __CONF_FILES SPECS... + # conf_load_files "${__CONF_FILES[@]}" + local -a __CONF_FILES + conf_find_files __CONFS_FILES "$@" + conf_load_files "${__CONFS_FILES[@]}" +} + +function conf_find_files() { + # initialiser le tableau $1 avec les fichiers de configuration correspondant + # aux arguments $2..@ + # - si on spécifie un fichier, il est pris tel quel s'il existe + # - si on spécifie un répertoire, tous les fichiers *.conf de ce répertoire + # sont pris + # - si on spécifie un pattern e.g path/to/*.conf alors tous les fichiers + # correspondant au pattern sont pris + # - sinon l'argument est ignoré + local __conf_dest="$1"; shift + local -a __conf_files + local __conf_spec __conf_dir __conf_wc + array_new "$__conf_dest" + for __conf_spec in "$@"; do + if [ -f "$__conf_spec" ]; then + array_add "$__conf_dest" "$__conf_spec" + continue + elif [ -d "$__conf_spec" ]; then + __conf_spec="$__conf_spec/*.conf" + fi + splitwcs "$__conf_spec" __conf_dir __conf_wc + array_lsfiles __conf_files "${__conf_dir:-.}" "$__conf_wc" + array_extend "$__conf_dest" __conf_files + done +} + +function conf_load_files() { + # sourcer les fichiers spécifiés en faisant ce qui est nécessaire pour que + # les variables de __CONF_ARRAY_VARS soient correctement traitées. + local -a __conf_backups __conf_values + local __conf_file __conf_name __conf_i __conf_backup __conf_bn __conf_bv + for __conf_file in "$@"; do + # faire une copie de sauvegarde puis supprimer les variables tableaux + __conf_backups=() + for __conf_name in "${__CONF_ARRAY_VARS[@]}" "${__CONF_PATH_VARS[@]}"; do + __conf_backup="$(declare -p "$__conf_name" 2>/dev/null)" + if [ -z "$__conf_backup" ]; then + __conf_backup="$__conf_name=" + else + # faire une correction de l'expression parce que la commande + # affichée par declare -p est différente entre bash 4.3 et bash + # 4.4 pour les tableaux. soit le tableau array=(a b) + # - bash 4.3 affiche declare -a array='([0]="a" [1]="b")' + # - bash 4.4 affiche declare -a array=([0]="a" [1]="b") + __conf_backup="${__conf_backup#declare }" + __conf_bn="${__conf_backup%% *}" + __conf_bv="${__conf_backup#* }" + if [[ "$__conf_bn" == -*a* ]]; then + __conf_bn="${__conf_bv%%=*}" + __conf_bv="${__conf_bv#*=}" + if [ "${__conf_bv:0:2}" == "'(" -a "${__conf_bv: -2:2}" == ")'" ]; then + __conf_backup="$__conf_bn=$(eval "echo $__conf_bv")" + else + __conf_backup="$__conf_bn=$__conf_bv" + fi + else + __conf_backup="$__conf_bv" + fi + fi + __conf_backups=("${__conf_backups[@]}" "$__conf_backup") + unset "$__conf_name" + done + # charger le fichier + source "$__conf_file" + # puis restaurer les variables ou les fusionner avec une éventuelle nouvelle valeur + __conf_i=0 + for __conf_name in "${__CONF_ARRAY_VARS[@]}" "${__CONF_PATH_VARS[@]}"; do + __conf_backup="${__conf_backups[$__conf_i]}" + if [ -n "$(declare -p "$__conf_name" 2>/dev/null)" ]; then + # la variable a été redéfinie, la fusionner avec la précédente valeur + if array_contains __CONF_ARRAY_VARS "$__conf_name"; then + array_copy __conf_values "$__conf_name" + eval "$__conf_backup" + array_extend "$__conf_name" __conf_values + elif array_contains __CONF_PATH_VARS "$__conf_name"; then + __conf_values="${!__conf_name}" + eval "$__conf_backup" + uaddpath "$__conf_values" "$__conf_name" + fi + else + # la variable n'a pas été redéfinie, restaurer la précédente valeur + eval "$__conf_backup" + fi + __conf_i=$(($__conf_i + 1)) + done + done +} + +function conf_install() { + # USAGE: conf_install DEST SRCS... + # installer les fichiers de SRCS dans le répertoire standardisé DEST + # ## destination + # - si DEST est un nom sans chemin, e.g NAME, alors la destination est + # ~/etc/NAME.d + # - si DEST est un nom avec chemin, alors la valeur est prise telle quelle + # comme destination, et le répertoire est créé le cas échéant. + # Si un fichier existe déjà dans la destination, afficher une demande de + # confirmation avant de l'écraser + # ## source + # - si SRC est un fichier, le prendre tel quel + # - si SRC est un répertoire, prendre tous les fichiers SRC/*.conf + # - si SRC est un pattern, prendre tous les fichiers correspondant + local -a srcs + local src dir wc + local dest="$1"; shift + [[ "$dest" == */* ]] || dest="$HOME/etc/$dest.d" + mkdir -p "$dest" || return 1 + for src in "$@"; do + if [ -f "$src" ]; then + srcs=("$src") + elif [ -d "$src" ]; then + array_lsfiles srcs "$src" "*.conf" + else + splitwcs "$src" dir wc + array_lsfiles srcs "$dir" "$wc" + fi + for src in "${srcs[@]}"; do + copy_update_ask -y "$src" "$dest" + done + done +} + +function conf_upgrade() { + # USAGE: conf_upgrade DEST VARS... + # Si les variables VARS... sont spécifiées, on appelle au préalable conf_init() + local dest="$1"; shift + if [ $# -gt 0 ]; then + eval "$(conf_local)" + conf_init "$@" + fi + local desc namevalue name value + # calculer le préfixe et initialiser le fichier le cas échéant + if [ ! -f "$dest" ]; then + local prefix + for desc in "${__CONF_DESCS[@]}"; do + [ "${desc:0:1}" == "#" ] && prefix="$desc" + break + done + [ ${#prefix} -gt 0 ] || prefix="# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8" + echo "$prefix" >"$dest" + fi + # vérifier la présence de chaque variable + for desc in "${__CONF_DESCS[@]}"; do + [ "${desc:0:1}" == "#" ] && continue + splitfsep "$desc" // namevalue desc + splitvar "$namevalue" name value + if ! grep -qE "^\s*#*(\s*export)?\s*$name=" "$dest"; then + echo >>"$dest" + [ -n "$desc" ] && echo "# $desc" >>"$dest" + echo -n "#" >>"$dest" + echo_setv "$name" "$value" >>"$dest" + fi + done +} diff --git a/ufile b/ufile index b87b156..e45641b 100755 --- a/ufile +++ b/ufile @@ -1,7 +1,7 @@ #!/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 +urequire DEFAULTS multiconf function display_help() { uecho "$scriptname: classer des fichiers selon certains règles @@ -37,6 +37,7 @@ forme pattern:destdir[:renamef] USAGE $scriptname [options] + $scriptname [options] -r OPTIONS -c, --config CONFIG @@ -56,6 +57,9 @@ OPTIONS 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. + -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. -n, --fake Afficher les opérations qui seraient faites -l, --list @@ -116,6 +120,7 @@ args=(% --force-cp force_cp=1 -S:,--ssh: SSH= --force-scp force_scp=1 + -r,--recursive recursive=1 -n,--fake fake=1 -l,--list action=list -e,--edit action=edit @@ -126,8 +131,7 @@ parse_args "$@"; set -- "${args[@]}" ## charger toutes les règles -RULES=() -INCLUDES=() +conf_init -a RULES if [ -n "$config" ]; then if [ "$action" != edit ]; then # le fichier doit exister, sauf en mode édition où il sera créé s'il @@ -140,22 +144,11 @@ if [ -n "$config" ]; then else set_defaults ufile fi -array_copy rules RULES -for include in "${INCLUDES[@]}"; do - if [ -f "$include" ]; then - RULES=() - source "$include" - array_extend rules RULES - else - ewarn "$include: fichier introuvable" - fi -done -array_copy RULES rules +conf_load "$HOME/etc/ufile.d/*.conf" ## actions particulières if [ "$action" == list ]; then - echo "# $(echo_seta2 INCLUDES)" array_to_lines RULES exit 0 elif [ "$action" == edit ]; then @@ -180,7 +173,7 @@ else fi [ $# -gt 0 ] || die "Vous devez spécifier des fichiers à classer" -[ ${#RULES[*]} -gt 0 ] || die "Vous devez spécifier des règles pour le classement des fichiers dans ~/etc/default/ufile" +[ ${#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" # vérifier les règles for rule in "${RULES[@]}"; do @@ -194,13 +187,28 @@ for rule in "${RULES[@]}"; do fi done +# faire la liste des fichiers +files=() +for file in "$@"; do + 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 + eerror "$file: fichier introuvable. il sera ignoré" + fi +done + # faire le classement effectif r= -for file in "$@"; do - [ -f "$file" -o -n "$fake" ] || { - eerror "$file: fichier introuvable. il sera ignoré" - continue - } +for file in "${files[@]}"; do setx pf=abspath "$file" setx dir=dirname -- "$pf" setx filename=basename -- "$pf" diff --git a/uscrontab b/uscrontab index e0ca551..b4f60f6 100755 --- a/uscrontab +++ b/uscrontab @@ -234,6 +234,9 @@ OPTIONS $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 @@ -248,6 +251,9 @@ OPTIONS -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 @@ -286,6 +292,14 @@ OPTIONS AVANCEES 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 @@ -345,8 +359,10 @@ lockfile=auto lockdelay= fake= continuous= +force_ctnow= pause= force= +osvars=() parse_opts "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ -A,--add,--install action=install \ @@ -356,25 +372,50 @@ parse_opts "${PRETTYOPTS[@]}" \ -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" -uscrontab="$1"; shift - 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 @@ -436,10 +477,6 @@ elif [ "$action" == "remove" ]; then exit 0 elif [ "$action" == "list" ]; then - if check_pause "$pausefile" "$now"; then - ewarn "En pause. Réactiver avec $scriptname -Y" - fi - 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 }')" @@ -472,11 +509,18 @@ elif [ "$action" == "list" ]; then 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" @@ -569,7 +613,7 @@ elif [ "$action" == "run" ]; then local cmd cmd="${1%% *}" case "$cmd" in - check_pidfile|remove_pidfile|elogto) return 1;; + check_pidfile|remove_pidfile|osvar|elogto) return 1;; esac return 0 } @@ -604,6 +648,21 @@ elif [ "$action" == "run" ]; then 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" @@ -648,6 +707,17 @@ puis supprimez le cas échéant le fichier $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 diff --git a/uwatch b/uwatch index ad6ad06..43e08ac 100755 --- a/uwatch +++ b/uwatch @@ -18,6 +18,10 @@ OPTIONS Afficher un décompte depuis DURATION jusqu'à 0 puis terminer le script. DURATION exprimé en secondes est le premier argument du script et vaut par défaut ${DEFAULT_DURATION} soit $(($DEFAULT_DURATION / 60)) minutes. + -f, --wfile FILE + Arrêter dès que le fichier FILE n'existe plus + -p, --wpid PID + Arrêter dès que la processus PID n'existe plus -u, --units Avec l'option --count, afficher l'unité: sec., min. ou heures -o, --offset NBSEC @@ -42,6 +46,8 @@ DEFAULT_STEP=1 DEFAULT_DURATION=300 what=time +wfile= +wpid= units= offset= step= @@ -52,6 +58,8 @@ args=( -t,--time what=time -c,--count what=count -r,--rcount what=rcount + -f:,--wfile: wfile= + -p:,--wpid: wpid= -u,--units units=1 -o:,--offset: offset= -s:,--step: step= @@ -112,5 +120,13 @@ while true; do msg="$msg (jusqu'à $until)" esac echo -n $'\e[1G\e[K'"$prefix$msg$suffix" + if [ -n "$wfile" -a ! -f "$wfile" ]; then + echo + break + fi + if [ -n "$wpid" ] && ! kill -0 "$wpid" 2>/dev/null; then + echo + break + fi sleep "$step" done