nutools/lib/nulib/bash/base.eval

469 lines
14 KiB
Bash

# -*- 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" ]]'
}