# -*- 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 "$@" } 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 "$@" } 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 "$@"; }