469 lines
14 KiB
Plaintext
469 lines
14 KiB
Plaintext
|
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
||
|
##@cooked nocomments
|
||
|
module: base.eval base_ "Fonctions de base: évaluation d'expressions"
|
||
|
require: base.str base.arr
|
||
|
|
||
|
################################################################################
|
||
|
# Chaines
|
||
|
|
||
|
function: base_evals "Appliquer à une chaine de caractères une suite de traitements, e.g:
|
||
|
~~~
|
||
|
base_evals var deref +suffix
|
||
|
~~~
|
||
|
est équivalent à
|
||
|
~~~
|
||
|
echo \"\${var}suffix\"
|
||
|
~~~
|
||
|
|
||
|
En commençant avec la valeur initiale \$1, les arguments \$2..* sont des
|
||
|
opérations à appliquer dans l'ordre.
|
||
|
|
||
|
Les opérations suivantes considèrent que la valeur courante est un nom de
|
||
|
variable:
|
||
|
~~~
|
||
|
:- := :? :+ deref dcount
|
||
|
~~~
|
||
|
|
||
|
Toutes les autres opérations travaillent directement avec la valeur
|
||
|
courante. Les opérations suivantes appliquent une transformation:
|
||
|
~~~
|
||
|
# % / : ^ , +# -# +% -% + - mid repl
|
||
|
~~~
|
||
|
IMPORTANT: aucune de ces fonctions ne met en échappement les valeur des
|
||
|
patterns. Ainsi, si un pattern contient des caractères interdits comme \\ ou \$,
|
||
|
il faut d'abord le traiter avec _qval()
|
||
|
|
||
|
Les opérations suivantes font un test sur la valeur et retournent immédiatement:
|
||
|
~~~
|
||
|
= == != < > -eq -ne -lt -le -gt -ge -n -z
|
||
|
~~~
|
||
|
|
||
|
La syntaxe des opérateurs standards de bash est reprise autant que possible, i.e
|
||
|
si on a l'habitude d'écrire ${varOP} en bash, alors la syntaxe à utiliser à
|
||
|
priori est 'base_evals var OP' ou 'base_evals var deref OP' suivant les
|
||
|
opérateurs.
|
||
|
|
||
|
Autres opérateurs:
|
||
|
~~~
|
||
|
deref indirection
|
||
|
dcount nombre d'éléments du tableau
|
||
|
+#STR ajouter un préfixe
|
||
|
-#STR supprimer un préfixe
|
||
|
+%STR ou +STR ajouter un suffixe
|
||
|
-%STR ou -STR supprimer un suffixe
|
||
|
mid RANGE traiter la chaine avec base_strmid()
|
||
|
repl FROM TO traiter la chaine avec base_strrepl()
|
||
|
~~~
|
||
|
|
||
|
Tout autre opérateur est traité comme un appel à une fonction qui prend un seul
|
||
|
argument, la valeur courante, et qui affiche le résultat."
|
||
|
function base_evals() {
|
||
|
local -a es__tmp
|
||
|
local es__value="$1"; shift
|
||
|
while [ $# -gt 0 ]; do
|
||
|
case "$1" in
|
||
|
# l'argument est le nom de la variable
|
||
|
:-*|:=*|:\?*|:+*) eval 'es__value="${'"${es__value}$1"'}"';;
|
||
|
d|deref) es__value="${!es__value}";;
|
||
|
dc|dcount|ds|dsize)
|
||
|
es__value="${es__value}[@]"
|
||
|
es__tmp=("${!es__value}")
|
||
|
es__value="${#es__tmp[@]}"
|
||
|
;;
|
||
|
# l'argument est la valeur de la variable
|
||
|
\#*|%*|/*|:*|^*|,*) eval 'es__value="${es__value'"$1"'}"';;
|
||
|
l|length) es__value="${#es__value}";;
|
||
|
=|==|!=|\<|\>|-eq|-ne|-lt|-le|-gt|-ge)
|
||
|
es__tmp=(\[ "$es__value" "$@" ]); "${es__tmp[@]}"; return $?;;
|
||
|
-n|-z) es__tmp=(\[ "$1" "$es__value" ]); "${es__tmp[@]}"; return $?;;
|
||
|
+#*) eval 'es__value="'"${1#+#}"'$es__value"';;
|
||
|
-#*) eval 'es__value="${es__value'"${1#-}"'}"';;
|
||
|
+%*) eval 'es__value="$es__value"'"${1#+%}";;
|
||
|
+*) eval 'es__value="$es__value"'"${1#+}";;
|
||
|
-%*) eval 'es__value="${es__value'"${1#-}"'}"';;
|
||
|
-*) eval 'es__value="${es__value%'"${1#-}"'}"';;
|
||
|
mid|strmid|base_strmid) eval 'es__value="$(base_strmid "$2" "$es__value")"'; shift;;
|
||
|
repl|strrepl|base_strrepl) eval 'es__value="$(base_strrepl "$2" "$3" "$es__value")"'; shift; shift;;
|
||
|
*) es__value="$("$1" "$es__value")";;
|
||
|
esac
|
||
|
shift
|
||
|
done
|
||
|
echo "$es__value"
|
||
|
}
|
||
|
|
||
|
function: base_setxs "équivalent à setx \$1 evals \$2..@"
|
||
|
function base_setxs() {
|
||
|
local -a ss__args
|
||
|
if [ "$1" == -a ]; then ss__args=(-a); shift; fi
|
||
|
local ss__var="$1"; shift
|
||
|
if [[ "$ss__var" == *=* ]]; then
|
||
|
set -- "${ss__var#*=}" "$@"
|
||
|
ss__var="${ss__var%%=*}"
|
||
|
fi
|
||
|
ss__args=("${ss__args[@]}" "$ss__var")
|
||
|
setx "${ss__args[@]}" base_evals "$@"
|
||
|
}
|
||
|
|
||
|
function: base_cmds "lancer une commande avec comme argument le résultat de evals
|
||
|
|
||
|
Par exemple, les deux commandes suivantes sont équivalentes:
|
||
|
~~~
|
||
|
base_cmds CMD ARGS... // EVALARGS
|
||
|
CMD ARGS... \"\$(evals EVALARGS)\"
|
||
|
~~~"
|
||
|
function base_cmds() {
|
||
|
local cs__arg
|
||
|
local -a cs__cmd
|
||
|
while [ $# -gt 0 ]; do
|
||
|
cs__arg="$1"; shift
|
||
|
[ "$cs__arg" == // ] && break
|
||
|
cs__cmd=("${cs__cmd[@]}" "$cs__arg")
|
||
|
done
|
||
|
"${cs__cmd[@]}" "$(base_evals "$@")"
|
||
|
}
|
||
|
|
||
|
function: base_evalm "construire une chaine en mixant chaines statiques et évaluations de commandes
|
||
|
|
||
|
Par exemple, les deux commandes suivantes sont équivalentes:
|
||
|
~~~
|
||
|
evalm //\"string\" cmd args // cmd args //\"string\"
|
||
|
echo \"string\$(cmd args)\$(cmd args)string\"
|
||
|
~~~"
|
||
|
function base_evalm() {
|
||
|
local em__val em__arg
|
||
|
local -a em__cmd
|
||
|
while [ $# -gt 0 ]; do
|
||
|
em__arg="$1"
|
||
|
if [ "${em__arg#//}" != "$em__arg" ]; then
|
||
|
em__val="$em__val${em__arg#//}"
|
||
|
shift
|
||
|
continue
|
||
|
fi
|
||
|
em__cmd=()
|
||
|
while [ $# -gt 0 ]; do
|
||
|
em__arg="$1"
|
||
|
[ "${em__arg#//}" != "$em__arg" ] && break
|
||
|
shift
|
||
|
if [ "${em__arg%//}" != "$em__arg" ]; then
|
||
|
local em__tmp="${em__arg%//}"
|
||
|
if [ -z "${em__tmp//\\/}" ]; then
|
||
|
em__arg="${em__arg#\\}"
|
||
|
em__cmd=("${em__cmd[@]}" "$em__arg")
|
||
|
continue
|
||
|
fi
|
||
|
fi
|
||
|
em__cmd=("${em__cmd[@]}" "$em__arg")
|
||
|
done
|
||
|
[ ${#em__cmd[*]} -gt 0 ] && em__val="$em__val$("${em__cmd[@]}")"
|
||
|
done
|
||
|
echo "$em__val"
|
||
|
}
|
||
|
|
||
|
function: base_setxm "équivalent à setx \$1 evalm \$2..@"
|
||
|
function base_setxm() {
|
||
|
local -a sm__args
|
||
|
if [ "$1" == -a ]; then sm__args=(-a); shift; fi
|
||
|
local sm__var="$1"; shift
|
||
|
if [[ "$sm__var" == *=* ]]; then
|
||
|
set -- "${sm__var#*=}" "$@"
|
||
|
sm__var="${sm__var%%=*}"
|
||
|
fi
|
||
|
sm__args=("${sm__args[@]}" "$sm__var")
|
||
|
setx "${sm__args[@]}" base_evalm "$@"
|
||
|
}
|
||
|
|
||
|
function: base_cmdm "lancer une commande avec comme argument le résultat de evalm
|
||
|
|
||
|
Par exemple, les deux commandes suivantes sont équivalentes:
|
||
|
~~~
|
||
|
base_cmdm CMD ARGS... // EVALARGS
|
||
|
CMD ARGS... \"\$(evalm EVALARGS)\"
|
||
|
~~~"
|
||
|
function base_cmdm() {
|
||
|
local cm__arg
|
||
|
local -a cm__cmd
|
||
|
while [ $# -gt 0 ]; do
|
||
|
cm__arg="$1"; shift
|
||
|
[ "$cm__arg" == // ] && break
|
||
|
cm__cmd=("${cm__cmd[@]}" "$cm__arg")
|
||
|
done
|
||
|
"${cm__cmd[@]}" "$(base_evalm "$@")"
|
||
|
}
|
||
|
|
||
|
################################################################################
|
||
|
# Nombres
|
||
|
|
||
|
function: base_evali "Evaluer une expression numérique"
|
||
|
function base_evali() {
|
||
|
echo "$(($*))"
|
||
|
}
|
||
|
|
||
|
################################################################################
|
||
|
# Tableaux
|
||
|
|
||
|
################################################################################
|
||
|
# Composition
|
||
|
|
||
|
function: base_evalc "Implémenter une syntaxe lisible et naturelle permettant d'enchainer des traitements sur une valeur.
|
||
|
|
||
|
Par exemple, la commande
|
||
|
~~~
|
||
|
evalc cmd1... // cmd2... // cmd3...
|
||
|
~~~
|
||
|
est équivalente à la commande
|
||
|
~~~
|
||
|
cmd3... \"\$(cmd2... \"\$(cmd1...)\")\"
|
||
|
~~~"
|
||
|
function base_evalc() {
|
||
|
local ec__arg ec__cmd ec__finalcmd
|
||
|
|
||
|
while [ $# -gt 0 ]; do
|
||
|
ec__arg="$1"; shift
|
||
|
if [ "$ec__arg" == // ]; then
|
||
|
if [ ${#ec__cmd} -gt 0 ]; then
|
||
|
if [ ${#ec__finalcmd} -eq 0 ]; then ec__finalcmd="$ec__cmd"
|
||
|
else ec__finalcmd="$ec__cmd \$($ec__finalcmd)"
|
||
|
fi
|
||
|
fi
|
||
|
ec__cmd=
|
||
|
continue
|
||
|
elif [ "${ec__arg%//}" != "$ec__arg" ]; then
|
||
|
local tmp="${ec__arg%//}"
|
||
|
[ -z "${tmp//\\/}" ] && ec__arg="${ec__arg#\\}"
|
||
|
fi
|
||
|
ec__cmd="$ec__cmd \"$(_qval "$ec__arg")\""
|
||
|
done
|
||
|
if [ ${#ec__cmd} -gt 0 ]; then
|
||
|
if [ ${#ec__finalcmd} -eq 0 ]; then ec__finalcmd="$ec__cmd"
|
||
|
else ec__finalcmd="$ec__cmd \$($ec__finalcmd)"
|
||
|
fi
|
||
|
fi
|
||
|
eval "$ec__finalcmd"
|
||
|
}
|
||
|
|
||
|
function: base_setxc "équivalent à setx \$1 evalc \$2..@"
|
||
|
function base_setxc() {
|
||
|
local -a sx__args
|
||
|
if [ "$1" == -a ]; then sx__args=(-a); shift; fi
|
||
|
local sx__var="$1"; shift
|
||
|
if [[ "$sx__var" == *=* ]]; then
|
||
|
set -- "${sx__var#*=}" "$@"
|
||
|
sx__var="${sx__var%%=*}"
|
||
|
fi
|
||
|
sx__args=("${sx__args[@]}" "$sx__var")
|
||
|
setx "${sx__args[@]}" base_evalc "$@"
|
||
|
}
|
||
|
|
||
|
################################################################################
|
||
|
# Chainage
|
||
|
|
||
|
function: base_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."
|
||
|
function base_evalp() {
|
||
|
local ep__arg ep__cmd
|
||
|
|
||
|
while [ $# -gt 0 ]; do
|
||
|
ep__arg="$1"; shift
|
||
|
if [ "$ep__arg" == // ]; then
|
||
|
ep__cmd="$ep__cmd |"
|
||
|
continue
|
||
|
elif [ "${ep__arg%//}" != "$ep__arg" ]; then
|
||
|
local ep__tmp="${ep__arg%//}"
|
||
|
if [ -z "${ep__tmp//\\/}" ]; then
|
||
|
ep__arg="${ep__arg#\\}"
|
||
|
fi
|
||
|
fi
|
||
|
ep__cmd="${ep__cmd:+$ep__cmd }\"$(_qval "$ep__arg")\""
|
||
|
done
|
||
|
eval "$ep__cmd"
|
||
|
}
|
||
|
|
||
|
function: base_setxp "équivalent à setx \$1 evalp \$2..@"
|
||
|
function base_setxp() {
|
||
|
local -a sp__args
|
||
|
if [ "$1" == -a ]; then sp__args=(-a); shift; fi
|
||
|
local sp__var="$1"; shift
|
||
|
if [[ "$sp__var" == *=* ]]; then
|
||
|
set -- "${sp__var#*=}" "$@"
|
||
|
sp__var="${sp__var%%=*}"
|
||
|
fi
|
||
|
sp__args=("${sp__args[@]}" "$sp__var")
|
||
|
setx "${sp__args[@]}" base_evalp "$@"
|
||
|
}
|
||
|
|
||
|
function: base_cmdp "lancer une commande avec comme argument le résultat de evalp
|
||
|
|
||
|
Par exemple, les deux commandes suivantes sont équivalentes:
|
||
|
~~~
|
||
|
base_cmdp CMD ARGS... // EVALARGS
|
||
|
CMD ARGS... \"\$(evalp EVALARGS)\"
|
||
|
~~~"
|
||
|
function base_cmdp() {
|
||
|
local cp__arg
|
||
|
local -a cp__cmd
|
||
|
while [ $# -gt 0 ]; do
|
||
|
cp__arg="$1"; shift
|
||
|
[ "$cp__arg" == // ] && break
|
||
|
cp__cmd=("${cp__cmd[@]}" "$cp__arg")
|
||
|
done
|
||
|
"${cp__cmd[@]}" "$(base_evalp "$@")"
|
||
|
}
|
||
|
|
||
|
################################################################################
|
||
|
# Générique
|
||
|
|
||
|
function: base_evalx ""
|
||
|
function base_evalx() {
|
||
|
:
|
||
|
}
|
||
|
|
||
|
function: base_setxx "équivalent à setx \$1 evalx \$2..@"
|
||
|
function base_setxx() {
|
||
|
local -a sx__args
|
||
|
if [ "$1" == -a ]; then sx__args=(-a); shift; fi
|
||
|
local sx__var="$1"; shift
|
||
|
if [[ "$sx__var" == *=* ]]; then
|
||
|
set -- "${sx__var#*=}" "$@"
|
||
|
sx__var="${sx__var%%=*}"
|
||
|
fi
|
||
|
sx__args=("${sx__args[@]}" "$sx__var")
|
||
|
setx "${sx__args[@]}" base_evalx "$@"
|
||
|
}
|
||
|
|
||
|
function: base_cmdx "lancer une commande avec comme argument le résultat de evalx
|
||
|
|
||
|
Par exemple, les deux commandes suivantes sont équivalentes:
|
||
|
~~~
|
||
|
base_cmdx CMD ARGS... // EVALARGS
|
||
|
CMD ARGS... \"\$(evalx EVALARGS)\"
|
||
|
~~~"
|
||
|
function base_cmdx() {
|
||
|
local cx__arg
|
||
|
local -a cx__cmd
|
||
|
while [ $# -gt 0 ]; do
|
||
|
cx__arg="$1"; shift
|
||
|
[ "$cx__arg" == // ] && break
|
||
|
cx__cmd=("${cx__cmd[@]}" "$cx__arg")
|
||
|
done
|
||
|
"${cx__cmd[@]}" "$(base_evalx "$@")"
|
||
|
}
|
||
|
|
||
|
function: base_cmdsplitf "\
|
||
|
Cette fonction doit être appelée avec N arguments (avec N>1). Elle analyse et
|
||
|
découpe l'argument \$N comme avec une ligne de commande du shell. Ensuite, elle
|
||
|
appelle la fonction \$1 avec les arguments de \$2 à \${N-1}, suivi des arguments
|
||
|
obtenus lors de l'analyse de l'argument \$N. Par exemple, la commande suivante:
|
||
|
~~~
|
||
|
strsplitf cmd arg1 \"long arg2\" \"arg3 'long arg4'\"
|
||
|
~~~
|
||
|
est équivalente à:
|
||
|
~~~
|
||
|
cmd arg1 \"long arg2\" arg3 \"long arg4\"
|
||
|
~~~
|
||
|
|
||
|
Retourner le code 127 si la fonction à appeler n'est pas spécifiée. Retourner le
|
||
|
code 126 si une erreur s'est produite lors de l'analyse de l'argument \$N"
|
||
|
function base_cmdsplitf() {
|
||
|
[ $# -gt 0 ] || return 127
|
||
|
local func count
|
||
|
func="$1"; shift
|
||
|
count=$#
|
||
|
if [ $count -gt 0 ]; then
|
||
|
eval 'set -- "${@:1:$(($count-1))}" '"${!count}" || return 126
|
||
|
fi
|
||
|
"$func" "$@"
|
||
|
}
|
||
|
|
||
|
################################################################################
|
||
|
# Tests
|
||
|
|
||
|
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
|
||
|
~~~"
|
||
|
function testx() {
|
||
|
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
|
||
|
~~~"
|
||
|
function test2x() {
|
||
|
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
|
||
|
~~~"
|
||
|
function testrx() {
|
||
|
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
|
||
|
~~~"
|
||
|
function testp() {
|
||
|
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
|
||
|
~~~"
|
||
|
function test2p() {
|
||
|
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
|
||
|
~~~"
|
||
|
function testrp() {
|
||
|
local t__val1="$1"; shift
|
||
|
local t__op="$1"; shift
|
||
|
local t__val2="$(evalp "$@")"
|
||
|
eval '[[ "$t__val1" '"$t__op"' "$t__val2" ]]'
|
||
|
}
|