nutools/lib/nulib/bash/base.core.sh

459 lines
13 KiB
Bash

# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
##@cooked nocomments
module: base.core base_ "Fonctions de base: fondement"
function: echo_ "afficher la valeur \$* sans passer à la ligne"
function echo_() { 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)"
function recho() {
if [[ "${1:0:2}" == -[eEn] ]]; then
local first="${1:1}"; shift
echo -n -
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)"
function recho_() {
if [[ "${1:0:2}" == -[eEn] ]]; then
local first="${1:1}"; shift
echo -n -
echo -n "$first" "$@"
else
echo -n "$@"
fi
}
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"
function _qval() {
local s="$*"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//\$/\\\$}"
s="${s//\`/\\\`}"
recho_ "$s"
}
function: base_should_quote "Tester si la chaine \$* doit être mise entre quotes"
function base_should_quote() {
# pour optimiser, toujours mettre entre quotes si plusieurs arguments sont
# spécifiés ou si on spécifie une chaine vide ou de plus de 80 caractères
[ $# -eq 0 -o $# -gt 1 -o ${#1} -eq 0 -o ${#1} -gt 80 ] && return 0
# sinon, tester si la chaine contient des caractères spéciaux
local s="$*"
s="${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 \""
function qval() {
echo -n \"
_qval "$@"
echo \"
}
function: qvalm "Afficher la chaine \$* quotée si nécessaire avec \""
function qvalm() {
if base_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"
function qvalr() {
if [ -z "$*" ]; then
:
elif base_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 étant séparée par un espace"
function qvals() {
local arg first=1
for arg in "$@"; do
[ -z "$first" ] && echo -n " "
if base_should_quote "$arg"; then
echo -n \"
_qval "$arg"
echo -n \"
else
recho_ "$arg"
fi
first=
done
[ -z "$first" ] && echo
}
function: qwc "Dans la chaine \$*, remplacer:
~~~
\\ par \\\\
\" par \\\"
\$ par \\\$
\` par \\\`
~~~
puis quoter la chaine avec \", sauf les wildcards *, ? et [class]
Cela permet de quoter une chaine permettant de glober des fichiers, e.g
~~~
eval \"ls \$(qwc \"\$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"
function qwc() {
local s="$*"
s="${s//\\/\\\\}"
s="${s//\"/\\\"}"
s="${s//\$/\\\$}"
s="${s//\`/\\\`}"
local r a b c
while [ -n "$s" ]; do
a=; b=; c=
a=; [[ "$s" == *\** ]] && { a="${s%%\**}"; a=${#a}; }
b=; [[ "$s" == *\?* ]] && { b="${s%%\?*}"; b=${#b}; }
c=; [[ "$s" == *\[* ]] && { c="${s%%\[*}"; c=${#c}; }
if [ -z "$a" -a -z "$b" -a -z "$c" ]; then
r="$r\"$s\""
break
fi
if [ -n "$a" ]; then
[ -n "$b" ] && [ $a -lt $b ] && b=
[ -n "$c" ] && [ $a -lt $c ] && c=
fi
if [ -n "$b" ]; then
[ -n "$a" ] && [ $b -lt $a ] && a=
[ -n "$c" ] && [ $b -lt $c ] && c=
fi
if [ -n "$c" ]; then
[ -n "$a" ] && [ $c -lt $a ] && a=
[ -n "$b" ] && [ $c -lt $b ] && b=
fi
if [ -n "$a" ]; then # PREFIX*
a="${s%%\**}"
s="${s#*\*}"
[ -n "$a" ] && r="$r\"$a\""
r="$r*"
elif [ -n "$b" ]; then # PREFIX?
a="${s%%\?*}"
s="${s#*\?}"
[ -n "$a" ] && r="$r\"$a\""
r="$r?"
elif [ -n "$c" ]; then # PREFIX[class]
a="${s%%\[*}"
b="${s#*\[}"; b="${b%%\]*}"
s="${s:$((${#a} + ${#b} + 2))}"
[ -n "$a" ] && r="$r\"$a\""
r="$r[$b]"
fi
done
recho_ "$r"
}
function: qlines "Traiter chaque ligne de l'entrée standard pour en faire des chaines quotées avec '"
function qlines() {
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"
function setv() {
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()"
function _setv() {
local s__var="$1"; shift
eval "$s__var=\"\$*\""
}
function: echo_setv "Afficher la commande qui serait lancée par setv \"\$@\""
function echo_setv() {
local s__var="$1"; shift
if [[ "$s__var" == *=* ]]; then
set -- "${s__var#*=}" "$@"
s__var="${s__var%%=*}"
fi
echo "$s__var=$(qvalr "$*")"
}
function: echo_setv2 "Afficher la commande qui recrée la variable \$1.
Equivalent à
~~~
echo_setv \"\$1=\${!1}\"
~~~
Si d'autres arguments que le nom de la variable sont spécifiés, cette fonction
se comporte comme echo_setv()"
function echo_setv2() {
local s__var="$1"; shift
if [[ "$s__var" == *=* ]]; then
set -- "${s__var#*=}" "$@"
s__var="${s__var%%=*}"
fi
if [ $# -eq 0 ]; then
echo_setv "$s__var" "${!s__var}"
else
echo_setv "$s__var" "$@"
fi
}
function: seta "initialiser le tableau \$1 avec les valeurs \$2..@
note: en principe, la syntaxe est 'seta array values...'. cependant, la syntaxe
'seta array=values...' est supportée aussi"
function seta() {
local s__array="$1"; shift
if [[ "$s__array" == *=* ]]; then
set -- "${s__array#*=}" "$@"
s__array="${s__array%%=*}"
fi
eval "$s__array=(\"\$@\")"
}
function: _seta "Comme la fonction seta() mais ne supporte que la syntaxe '_seta array values...'
Cette fonction est légèrement plus rapide que seta()"
function _seta() {
local s__array="$1"; shift
eval "$s__array=(\"\$@\")"
}
function: echo_seta "Afficher la commande qui serait lancée par seta \"\$@\""
function echo_seta() {
local s__var="$1"; shift
if [[ "$s__var" == *=* ]]; then
set -- "${s__var#*=}" "$@"
s__var="${s__var%%=*}"
fi
echo "$s__var=($(qvals "$@"))"
}
function: echo_seta2 "Afficher la commande qui recrée le tableau \$1
Si d'autres arguments que le nom de tableau sont spécifiés, cette fonction se
comporte comme echo_seta()"
function echo_seta2() {
local s__var="$1"; shift
if [[ "$s__var" == *=* ]]; then
set -- "${s__var#*=}" "$@"
s__var="${s__var%%=*}"
elif [ $# -eq 0 ]; then
eval "set -- \"\${$s__var[@]}\""
fi
echo "$s__var=($(qvals "$@"))"
}
function: setx "Initialiser une variable avec le résultat d'une commande
* syntaxe 1: initialiser la variable \$1 avec le résultat de la commande \"\$2..@\"
~~~
setx var cmd
~~~
note: en principe, la syntaxe est 'setx var cmd args...'. cependant, la syntaxe
'setx var=cmd args...' est supportée aussi
* syntaxe 2: initialiser le tableau \$1 avec le résultat de la commande
\"\$2..@\", chaque ligne du résultat étant un élément du tableau
~~~
setx -a array cmd
~~~
note: en principe, la syntaxe est 'setx -a array cmd args...'. cependant, la
syntaxe 'setx -a array=cmd args...' est supportée aussi"
function setx() {
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."
function _setvx() {
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."
function _setax() {
local s__array="$1"; shift
eval "$s__array=($("$@" | qlines))"
}
function: base_is_defined "tester si la variable \$1 est définie"
function base_is_defined() {
[ -n "$(declare -p "$1" 2>/dev/null)" ]
}
function: base_is_array "tester si la variable \$1 est un tableau"
function base_is_array() {
[[ "$(declare -p "$1" 2>/dev/null)" =~ declare\ -[^\ ]*a[^\ ]*\ ]]
}
function: base_array_local "afficher les commandes pour faire une copie dans la variable locale \$1 du tableau \$2"
function base_array_local() {
if [ "$1" == "$2" ]; then
declare -p "$1" 2>/dev/null || echo "local -a $1"
else
echo "local -a $1; $1=(\"\${$2[@]}\")"
fi
}
function: base_upvar "Implémentation de upvar() de http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference
USAGE
~~~
local varname && base_upvar varname values...
~~~
* @param varname Variable name to assign value to
* @param values Value(s) to assign. If multiple values (> 1), an array is
assigned, otherwise a single value is assigned."
function base_upvar() {
if unset -v "$1"; then
if [ $# -lt 2 ]; then
eval "$1=\"\$2\""
else
eval "$1=(\"\${@:2}\")"
fi
fi
}
function: base_array_upvar "Comme base_upvar() mais force la création d'un tableau, même s'il y a que 0 ou 1 argument"
function base_array_upvar() {
unset -v "$1" && eval "$1=(\"\${@:2}\")"
}
function: base_upvars "Implémentation modifiée de upvars() de http://www.fvue.nl/wiki/Bash:_Passing_variables_by_reference
Par rapport à l'original, il n'est plus nécessaire de préfixer une variable
scalaire avec -v, et -a peut être spécifié sans argument.
USAGE
~~~
local varnames... && base_upvars [varname value | -aN varname values...]...
~~~
* @param -a assigns remaining values to varname as array
* @param -aN assigns next N values to varname as array. Returns 1 if wrong
number of options occurs"
function base_upvars() {
while [ $# -gt 0 ]; do
case "$1" in
-a)
unset -v "$2" && eval "$2=(\"\${@:3}\")"
break
;;
-a*)
unset -v "$2" && eval "$2=(\"\${@:3:${1#-a}}\")"
shift $((${1#-a} + 2)) || return 1
;;
*)
unset -v "$1" && eval "$1=\"\$2\""
shift; shift
;;
esac
done
}
function: base_set_debug "Passer en mode DEBUG"
function base_set_debug() {
export NULIB_DEBUG=1
}
function: base_is_debug "Tester si on est en mode DEBUG"
function base_is_debug() {
[ -n "$NULIB_DEBUG" ]
}
function: lawk "Lancer GNUawk avec la librairie 'base'"
function lawk() {
gawk -i base.awk "$@"
}
function: cawk "Lancer GNUawk avec LANG=C et la librairie 'base'
Le fait de forcer la valeur de LANG permet d'éviter les problèmes avec la locale"
function cawk() {
LANG=C gawk -i base.awk "$@"
}
function: lsort "Lancer sort avec support de la locale courante"
function: csort "Lancer sort avec LANG=C pour désactiver le support de la locale
Avec LANG!=C, sort utilise les règles de la locale pour le tri, et par
exemple, avec LANG=fr_FR.UTF-8, la locale indique que les ponctuations doivent
être ignorées."
function lsort() { sort "$@"; }
function csort() { LANG=C sort "$@"; }
function: lgrep "Lancer grep avec support de la locale courante"
function: cgrep "Lancer grep avec LANG=C pour désactiver le support de la locale"
function lgrep() { grep "$@"; }
function cgrep() { LANG=C grep "$@"; }
function: lsed "Lancer sed avec support de la locale courante"
function: csed "Lancer sed avec LANG=C pour désactiver le support de la locale"
function lsed() { sed "$@"; }
function csed() { LANG=C sed "$@"; }
function: ldiff "Lancer diff avec support de la locale courante"
function: cdiff "Lancer diff avec LANG=C pour désactiver le support de la locale"
function ldiff() { diff "$@"; }
function cdiff() { LANG=C diff "$@"; }