##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 ## Fonctions de base: fondement ##@cooked nocomments uprovide base.core function echo_() { # afficher la valeur $* sans passer à la ligne echo -n "$*" } function recho() { # afficher une valeur brute. contrairement à la commande echo, ne reconnaitre # aucune option (i.e. -e, -E, -n ne sont pas signifiants) if [[ "${1:0:2}" == -[eEn] ]]; then echo -n - local first="${1:1}"; shift echo "$first$@" else echo "$@" fi } function recho_() { # afficher une valeur brute, sans passer à la ligne. contrairement à la commande # echo, ne reconnaitre aucune option (i.e. -e, -E, -n ne sont pas signifiants) if [[ "${1:0:2}" == -[eEn] ]]; then echo -n - local first="${1:1}"; shift echo -n "$first$@" else echo -n "$@" fi } function _rval() { # Dans la chaine $*, remplacer \ par \\, " par \", $ par \$, ` par \`, SPACE par # \SPACE # Cela permet de quoter une chaine à afficher telle quelle, sans guillemets, e.g # eval "echo $(_rval "$value")" # Note: la protection de ! n'est pas effectuée, parce que le comportement du # shell est incohérent entre le shell interactif et les scripts. Pour une # version plus robuste, il est nécessaire d'utiliser un programme externe tel # que sed ou awk local s="$*" s="${s//\\/\\\\}" s="${s//\"/\\\"}" s="${s//\$/\\\$}" s="${s//\`/\\\`}" s="${s// /\\ }" recho_ "$s" } function _qval() { # Dans la chaine $*, remplacer \ par \\, " par \", $ par \$, ` par \` # Cela permet de quoter une chaine à mettre entre guillements. # Note: la protection de ! n'est pas effectuée, parce que le comportement du # shell est incohérent entre le shell interactif et les scripts. Pour une # version plus robuste, il est nécessaire d'utiliser un programme externe tel # que sed ou awk local s="$*" s="${s//\\/\\\\}" s="${s//\"/\\\"}" s="${s//\$/\\\$}" s="${s//\`/\\\`}" recho_ "$s" } function should_quote() { # Tester si la chaine $* doit être mise entre quotes local l="${#1}" # pour optimiser, toujours mettre entre quotes une chaine vide ou de plus de 80 caractères [ $l -eq 0 -o $l -gt 80 ] && return 0 # sinon, tester si la chaine contient des caractères spéciaux local s="${*//[a-zA-Z0-9]/}" s="${s//,/}" s="${s//./}" s="${s//+/}" s="${s//\//}" s="${s//-/}" s="${s//_/}" s="${s//=/}" [ -n "$s" ] } function qval() { # Afficher la chaine $* quotée avec " echo -n \" _qval "$@" echo \" } function qvalm() { # Afficher la chaine $* quotée si nécessaire avec " if should_quote "$*"; then echo -n \" _qval "$@" echo \" else recho "$*" fi } function qvalr() { # Afficher la chaine $* quotée si nécessaire avec ", sauf si elle est vide if [ -z "$*" ]; then : elif should_quote "$*"; then echo -n \" _qval "$@" echo \" else recho "$*" fi } function qvals() { # Afficher chaque argument de cette fonction quotée le cas échéant avec " # Chaque valeur est séparée par un espace. local arg first=1 for arg in "$@"; do [ -z "$first" ] && echo -n " " if should_quote "$arg"; then echo -n \" _qval "$arg" echo -n \" else recho_ "$arg" fi first= done [ -z "$first" ] && echo } function qlines() { # Traiter chaque ligne de l'entrée standard pour en faire des chaines quotées # avec ' sed "s/'/'\\\\''/g; s/.*/'&'/g" } function setv() { # initialiser la variable $1 avec la valeur $2* # note: en principe, la syntaxe est 'setv var values...'. cependant, la # syntaxe 'setv var=values...' est supportée aussi local __s_var="$1"; shift if [[ "$__s_var" == *=* ]]; then set -- "${__s_var#*=}" "$@" __s_var="${__s_var%%=*}" fi eval "$__s_var=\"\$*\"" } function _setv() { # Comme la fonction setv() mais ne supporte que la syntaxe '_setv var values...' # Cette fonction est légèrement plus rapide que setv() local __s_var="$1"; shift eval "$__s_var=\"\$*\"" } function echo_setv() { # Afficher la commande qui serait lancée par setv "$@" local __s_var="$1"; shift if [[ "$__s_var" == *=* ]]; then set -- "${__s_var#*=}" "$@" __s_var="${__s_var%%=*}" fi echo "$__s_var=$(qvalr "$*")" } function setx() { # syntaxe 1: setx var cmd # initialiser la variable $1 avec le résultat de la commande "$2..@" # note: en principe, la syntaxe est 'setx var cmd args...'. cependant, la # syntaxe 'setx var=cmd args...' est supportée aussi # syntaxe 2: setx -a array cmd # initialiser le tableau $1 avec le résultat de la commande "$2..@", chaque # ligne du résultat étant un élément du tableau # note: en principe, la syntaxe est 'setx -a array cmd args...'. cependant, la # syntaxe 'setx -a array=cmd args...' est supportée aussi if [ "$1" == -a ]; then shift local __s_array="$1"; shift if [[ "$__s_array" == *=* ]]; then set -- "${__s_array#*=}" "$@" __s_array="${__s_array%%=*}" fi eval "$__s_array=($("$@" | qlines))" else local __s_var="$1"; shift if [[ "$__s_var" == *=* ]]; then set -- "${__s_var#*=}" "$@" __s_var="${__s_var%%=*}" fi eval "$__s_var="'"$("$@")"' fi } function _setvx() { # Comme la fonction setx() mais ne supporte que l'initialisation d'une variable # scalaire avec la syntaxe '_setvx var cmd args...' pour gagner (un peu) en # rapidité d'exécution. local __s_var="$1"; shift eval "$__s_var="'"$("$@")"' } function _setax() { # Comme la fonction setx() mais ne supporte que l'initialisation d'un tableau # avec la syntaxe '_setax array cmd args...' pour gagner (un peu) en rapidité # d'exécution. local __s_array="$1"; shift eval "$__s_array=($("$@" | qlines))" } function evalx() { # Implémenter une syntaxe lisible et naturelle permettant d'enchainer des # traitements sur une valeur. Par exemple, la commande # evalx cmd1... // cmd2... // cmd3... # affiche le résultat de la commande "$(cmd3 $(cmd2 $(cmd1)))" # Retourner le dernier code d'erreur non nul, ou 0 si toutes les commandes se # sont exécutées sans erreur. local __e_val __e_arg __e_r=0 local -a __e_cmd local __e_first=1 while [ $# -gt 0 ]; do __e_cmd=() while [ $# -gt 0 ]; do __e_arg="$1"; shift [ "$__e_arg" == // ] && break if [ "${__e_arg%//}" != "$__e_arg" ]; then local __e_tmp="${__e_arg%//}" if [ -z "${__e_tmp//\\/}" ]; then __e_arg="${__e_arg#\\}" __e_cmd=("${__e_cmd[@]}" "$__e_arg") continue fi fi __e_cmd=("${__e_cmd[@]}" "$__e_arg") done if [ -n "$__e_first" ]; then __e_val="$("${__e_cmd[@]}")" || __e_r=$? else __e_val="$("${__e_cmd[@]}" "$__e_val")" || __e_r=$? fi __e_first= done [ -n "$__e_val" ] && echo "$__e_val" return $__e_r } function setxx() { # équivalent à setx $1 evalx $2..@ local -a __s_args if [ "$1" == -a ]; then __s_args=(-a); shift; fi local __s_var="$1"; shift if [[ "$__s_var" == *=* ]]; then set -- "${__s_var#*=}" "$@" __s_var="${__s_var%%=*}" fi __s_args=("${__s_args[@]}" "$__s_var") setx "${__s_args[@]}" evalx "$@" } function evalp() { # Implémenter une syntaxe alternative permettant d'enchainer des traitements sur # un flux de données. Par exemple, la commande # evalp cmd1... // cmd2... // cmd3... # affiche le résultat de la commande "$(cmd1 | cmd2 | cmd3)" # Typiquement, cette fonction permet de faciliter la construction d'un # enchainement de commandes par programme, ou de faciliter l'utilisation de la # fonction setx() pour récupérer le résultat d'un enchainement. Dans les autres # cas, il est plus simple et naturel d'écrire les enchainements avec la syntaxe # de bash. local __e_arg __e_cmd while [ $# -gt 0 ]; do __e_arg="$1"; shift if [ "$__e_arg" == // ]; then __e_cmd="$__e_cmd |" continue elif [ "${__e_arg%//}" != "$__e_arg" ]; then local __e_tmp="${__e_arg%//}" if [ -z "${__e_tmp//\\/}" ]; then __e_arg="${__e_arg#\\}" fi fi __e_cmd="${__e_cmd:+$__e_cmd }\"$(_qval "$__e_arg")\"" done eval "$__e_cmd" } function setxp() { # équivalent à setx $1 evalp $2..@ local -a __s_args if [ "$1" == -a ]; then __s_args=(-a); shift; fi local __s_var="$1"; shift if [[ "$__s_var" == *=* ]]; then set -- "${__s_var#*=}" "$@" __s_var="${__s_var%%=*}" fi __s_args=("${__s_args[@]}" "$__s_var") setx "${__s_args[@]}" evalp "$@" } function testx() { # Faire un test unaire avec la commande [ sur une valeur calculée avec evalx. # Utiliser la syntaxe 'testx op cmds...' e.g. # testx -z cmd1 // cmd2 local __t_op="$1"; shift local __t_val="$(evalx "$@")" [ $__t_op "$__t_val" ] } function test2x() { # Faire une test binaire avec la commande [ entre une valeur spécifiée et une # valeur calculée avec evalx. Utiliser la syntaxe 'test2x value op cmds...' e.g. # test2x value == cmd1 // cmd2 local __t_val1="$1"; shift local __t_op="$1"; shift local __t_val2="$(evalx "$@")" [ "$__t_val1" $__t_op "$__t_val2" ] } function testrx() { # Faire une test binaire avec la commande [[ entre une valeur spécifiée et une # valeur calculée avec evalx. Utiliser la syntaxe 'testrx value op cmds...' e.g. # testrx value == cmd1 // cmd2 local __t_val1="$1"; shift local __t_op="$1"; shift local __t_val2="$(evalx "$@")" eval '[[ "$__t_val1" '"$__t_op"' "$__t_val2" ]]' } function testp() { # Faire un test unaire avec la commande [ sur une valeur calculée avec evalp. # Utiliser la syntaxe 'testp op cmds...' e.g. # testp -z cmd1 // cmd2 local __t_op="$1"; shift local __t_val="$(evalp "$@")" [ $__t_op "$__t_val" ] } function test2p() { # Faire une test binaire avec la commande [ entre une valeur spécifiée et une # valeur calculée avec evalp. Utiliser la syntaxe 'test2p value op cmds...' e.g. # test2p value == cmd1 // cmd2 local __t_val1="$1"; shift local __t_op="$1"; shift local __t_val2="$(evalp "$@")" [ "$__t_val1" $__t_op "$__t_val2" ] } function testrp() { # Faire une test binaire avec la commande [[ entre une valeur spécifiée et une # valeur calculée avec evalp. Utiliser la syntaxe 'testrp value op cmds...' e.g. # testrp value == cmd1 // cmd2 local __t_val1="$1"; shift local __t_op="$1"; shift local __t_val2="$(evalp "$@")" eval '[[ "$__t_val1" '"$__t_op"' "$__t_val2" ]]' } function err2out() { # lancer la commande $@ en redirigeant la sortie d'erreur sur la sortie standard "$@" 2>&1 }