nulib/bash/base.eval.sh

469 lines
14 KiB
Bash
Raw Normal View History

2023-10-03 05:41:24 +04:00
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
##@cooked nocomments
module: base.eval "Fonctions de base: évaluation d'expressions"
require: base.str base.arr
################################################################################
# Chaines
function: evals "Appliquer à une chaine de caractères une suite de traitements, e.g:
~~~
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 'evals var OP' ou '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 strmid()
repl FROM TO traiter la chaine avec 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 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) eval 'es__value="$(strmid "$2" "$es__value")"'; shift;;
repl|strrepl) eval 'es__value="$(strrepl "$2" "$3" "$es__value")"'; shift; shift;;
*) es__value="$("$1" "$es__value")";;
esac
shift
done
echo "$es__value"
}
function: setxs "équivalent à setx \$1 evals \$2..@"
function 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[@]}" evals "$@"
}
function: cmds "lancer une commande avec comme argument le résultat de evals
Par exemple, les deux commandes suivantes sont équivalentes:
~~~
cmds CMD ARGS... // EVALARGS
CMD ARGS... \"\$(evals EVALARGS)\"
~~~"
function 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[@]}" "$(evals "$@")"
}
function: 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 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: setxm "équivalent à setx \$1 evalm \$2..@"
function 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[@]}" evalm "$@"
}
function: cmdm "lancer une commande avec comme argument le résultat de evalm
Par exemple, les deux commandes suivantes sont équivalentes:
~~~
cmdm CMD ARGS... // EVALARGS
CMD ARGS... \"\$(evalm EVALARGS)\"
~~~"
function 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[@]}" "$(evalm "$@")"
}
################################################################################
# Nombres
function: evali "Evaluer une expression numérique"
function evali() {
echo "$(($*))"
}
################################################################################
# Tableaux
################################################################################
# Composition
function: 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 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: setxc "équivalent à setx \$1 evalc \$2..@"
function 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[@]}" evalc "$@"
}
################################################################################
# Chainage
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."
function 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: setxp "équivalent à setx \$1 evalp \$2..@"
function 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[@]}" evalp "$@"
}
function: cmdp "lancer une commande avec comme argument le résultat de evalp
Par exemple, les deux commandes suivantes sont équivalentes:
~~~
cmdp CMD ARGS... // EVALARGS
CMD ARGS... \"\$(evalp EVALARGS)\"
~~~"
function 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[@]}" "$(evalp "$@")"
}
################################################################################
# Générique
function: evalx ""
function evalx() {
:
}
function: setxx "équivalent à setx \$1 evalx \$2..@"
function 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[@]}" evalx "$@"
}
function: cmdx "lancer une commande avec comme argument le résultat de evalx
Par exemple, les deux commandes suivantes sont équivalentes:
~~~
cmdx CMD ARGS... // EVALARGS
CMD ARGS... \"\$(evalx EVALARGS)\"
~~~"
function 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[@]}" "$(evalx "$@")"
}
function: 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 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" ]]'
}