[shell] Passage d'un tableau à une fonction

Passer un tableau à une fonction, une fonctionnalité de base pour les langages modernes, n’est pas si évident en shell. Surtout en bash comme vous le verrez plus bas. Si j’ai facilement trouvé une solution en ksh, il m’a fallu deux jours de réflexion et d’essais-erreurs pour y arriver en bash.

Objectif:[ol]
[li]créer deux tableaux (arrays)[/li]
[li]passer ces tableaux à une fonction qui, pour l’exemple, va simplement afficher leur contenu.[/li]
[li]assurer une portée locale des tableaux à l’intérieur de la fonction.[/li][/ol]

Solutions:[ul]
[li]On commence par le plus facile (ksh):

[code]#!/usr/bin/ksh

passage de tableau par référence à une fonction

function print_tableau {
# copie locale du tableau par référence aux paramètres $1 $2 etc…
typeset -n tab_local=$1

# affichage des éléments du tableau local
echo; echo "$1"
for item in ${!tab_local[@]}; do
    echo "   [${item}] => ${tab_local[$item]}"
done

}

affectation des tableaux

couleurs=('Rose' 'Gris clair' 'Vert')
etats=('Liquide' 'Solide' 'Gaz')

appel à la fonction avec passage des tableaux

print_tableau "couleurs"
print_tableau "etats"

vérification que le tableau “local” tab_local de la fonction n’existe plus ici

echo; echo "Contenu de tab_local -->${tab_local[*]}<--"

[/code]
[/li]
[li]Un peu (!) plus ardu en bash:

[code]#!/bin/bash

passage de tableau par référence à une fonction

function print_tableau {
# On met le IFS (Internal Field Separator) à NULL
# ceci est indispensable pour préserver l’espace dans “Gris clair”
# lors de l’expansion ${couleurs[*]} qui a lieu plus bas
OLD_IFS=$IFS
IFS=’’

# On crée une chaîne contenant "couleurs[*]"
local tab_str="$1[*]"

# on réaffecte le contenu du tableau $colors à tab_local par référence indirecte
    # une copie locale des tableaux "couleurs" et "etats" en quelque sorte
    # tab_local=(${couleurs[*]})
local tab_local=(${!tab_str})

# remise de IFS à sa valeur par défaut
IFS=$OLD_IFS

# affichage des éléments du tableau local
echo; echo "$1"
for item in ${!tab_local[@]}; do
    echo "   [${item}] => ${tab_local[$item]}"
done

}

affectation des tableaux

couleurs=('Rose' 'Gris clair' 'Vert')
etats=('Liquide' 'Solide' 'Gaz')

appel à la fonction avec passage des tableaux

print_tableau "couleurs"
print_tableau "etats"

vérification que le tableau “local” tab_local de la fonction n’existe plus ici

echo; echo "Contenu de tab_local -->${tab_local[*]}<--"

[/code]
[/li]
[li]Liens divers
unix.com/shell-programming-s … ction.html
tldp.org/LDP/abs/html/bashver2.html#VARREFNEW[/li][/ul]

Ceci peut aussi servir pour “copier” un tableau. Le réaffecter à une autre variable.

Vous comprenez pourquoi je préfère ksh? :smt002

Essaye en zsh, tu va être surpris :smiley:

J’ai deja essaye de lui dire ^^

Je ne demande qu’à voir. Allez-y, soumettez votre solution pour le problème ci-dessus. J’avoue que votre suggestion m’a poussé à aller voir ce qu’on dit de zsh sur le net et que ça m’a mis l’eau à la bouche. Mais, pour l’instant, je n’ai pas le courage de me mettre un troisième shell sur le dos.

Si vous vous prenez au jeu, on pourrait créer, à titre d’exercice ludique, une petite librairie de fonctions sur les arrays:

array_print
array_sort
array_search
array_merge

etc…

Le défi est lancé :wink:

Perso j’en ai pas besoin, mais honetement, regarde zsh, les autre shell sont inutiles :slightly_smiling:

[quote=“ed”]Perso j’en ai pas besoin, …[/quote]C’est clair que c’est juste un exercice de style et que si on a besoin de variables-tableaux on ferait mieux de se tourner vers Perl, Python, PHP-CLI ou même awk qui gère ça très bien.

[quote=“ed”]… regarde zsh, les autre shell sont inutiles :slightly_smiling:[/quote]J’ai regardé et ça m’a plu. Faudrait que je prenne le temps de tester. Il y a aussi le c shell qui a ses aficionados. Un jour sans doute…

bonsoir,
malheureusement, lire le manuel bash tout ce qui concerne section tableau t’aurais pris qu’une demi-heure, sans rien tester avant d’avoir trouver lol :wink:
attention pour les tableaux en bash, il y a un espace(+) entre parenthèse + contenu + parenthèse. (je crois même qu’il faut faire un echo dés fois, style dans la fonction ( “echo $*” ) [ <- c’est même plus simple que ça encore je crois bien ], enfin ya plus simple, me souviens plus trés bien.
C’est trés facile de passer un tableau en bash, mais sans savoir comment ou en l’ayant oublié, on cherche longtemps, sans le manuel, c’est vrai.

[quote]
array_print
array_sort
array_search
array_merge[/quote]
biensur pourquoi pas, je les ai déjà dans un coin…
dans le manuel (pas man), il y a de nombreux examples qui déjà mâchent bien le travail je crois.
petite question pur ed :
est-ce qu’on peut lancer un script autre que bash (ksh ?), via `` ou une fonction intégrée le tout via php ? à tester.

[quote=“usinagaz”]bonsoir,
malheureusement, lire le manuel bash tout ce qui concerne section tableau t’aurais pris qu’une demi-heure, sans rien tester avant d’avoir trouver lol :wink:
attention pour les tableaux en bash, il y a un espace(+) entre parenthèse + contenu + parenthèse. (je crois même qu’il faut faire un echo dés fois, style dans la fonction ( “echo $*” ) [ <- c’est même plus simple que ça encore je crois bien ], enfin ya plus simple, me souviens plus trés bien.
C’est trés facile de passer un tableau en bash, mais sans savoir comment ou en l’ayant oublié, on cherche longtemps, sans le manuel, c’est vrai.[/quote]

Je n’ai pas trop bien compris ta solution. Pourrais-tu développer en nous donner un exemple de passage de tableau en bash?

bonjour,
oui, en fait c’est simple, il faut juste savoir un truc:
en bash les variables ne sont pas typées.

ça veut dire que passer un tableau à une fonction, ce n’est pas exactement lui passer la réference d’un tableau, mais lui passer son contenu.
C’est ta fonction que tu conçois comme devant traiter la suite de variable comme un tableau, je pense.

exemple: mettons la fonction testTab(){}

on dit que tab=( 1 2 3 4 ) ou tab=(1 2 3 4) // tu avais raison, peu importe les espaces.

Que tu fasse
testTab ${tab[*]} ou
testTab 1 2 3 4
ça revient au même, on passe un contenu de tableau ou une suite d’argument à la fonction, et non un tableau en tant que tel.

Reste à reformer le tableau dans la fonction:
tab2=( $* ) tout simplement, ou
tab3=( echo $* ).
Si c’est sur echo $* que tu me demandais une explication, je ne sais plus pourquoi je faisais ça à un moment, mais il y avait une raison, peut-être mauvaise.

Voici une fonction qui pourrait faire office de array_sort, pour les autres, je retrouve plus, ou c’est pas montrable :frowning:; celle-ci est issue du manuel, si je me souviens bien :

[code]#!/bin/bash

Tri à bulle, des éléments d’un tableau .

tri_tableau() {

fonction interne, interversion des index -------------------------------------------------

echange()
{
# échange deux membres d’un tableau
local temp=${tab[$1]} # Stockage temporaire
# pour les éléments à échanger.
tab[$1]=${tab[$2]}
tab[$2]=$temp

return

}

------------------------------------------------------------------

test si un argument ----------------------------------------------------------------------

[ ! -n “$1” ] && {
echo "Absense de paramètre, spécifier un tableau"
exit $ERR_F_PARAM
}
tab=( $* ) # récupére le tableau
nombre_d_elements=${#tab[@]}
let "comparaisons = $nombre_d_elements - 1"
index=1 # Nombre de tours.
while [ “$comparaisons” -gt 0 ] # Début de la boucle externe.
do
index=0 # Réinitialise l’index pour commencer au début du tableau à chaque tour.

while [ "$index" -lt "$comparaisons" ]      # Début de la boucle interne.
do
  if [ ${tab[$index]} \> ${tab[`expr $index + 1`]} ]        #  Si non ordonné...
  then
    echange $index `expr $index + 1`        # échange.
  fi
  let "index += 1" # Ou index+=1 sur Bash 3.1 et suivants
done # Fin de la boucle interne.
let "comparaisons -= 1"                     #  retranche une comparaison (effectuée)
let "index += 1"                            # Incrémente le compteur de tour.

done # Fin de la boucle externe.
echo “${tab[@]}”; return; # “renvoie” le tableau trié
}
[/code]

Appeler une fonction en passant le contenu d’un tableau n’était pas l’objectif.

De plus, passer, comme tu le fais, le contenu d’un tableau dont un ou plusieurs éléments comportent des espaces pose problème. Passer le contenu du tableau (‘rouge’ ‘gris clair’ ‘vert’) tout en préservant l’espace de gris clair n’est pas si évident. Tu vas devoir jongler avec IFS. Enfin, si tu utilises, comme tu le fais, les paramètres positionnels $1 $2 etc… pour passer le contenu d’un tableau, comment feras-tu pour passer plusieurs tableaux à une fonction?

Le passage de tableau par référence est la seule solution. Même si, je le répète, il s’agit plus d’un exercice de style qu’autre chose car il existe des langages plus puissants pour ce genre de choses: perl, python, php etc… Et dans ce genre d’exercice de style, bash est lourd!

Signification : c’est de la pure masturbation intelectuelle.

Mais certains font pire! Il programment des shell comme zsh avec des histoires d’espaces de nom, de module à importer, intégrer ncurse en natif au script shell,… :open_mouth:

[quote=“MisterFreez”]Signification : c’est de la pure masturbation intelectuelle.

Mais certains font pire! Il programment des shell comme zsh avec des histoires d’espaces de nom, de module à importer, intégrer ncurse en natif au script shell,… :open_mouth:[/quote]

Ouaip! Il ne nous reste plus qu’à nous inscrire à l’Obfuscated Code Contest :smt030
en.wikipedia.org/wiki/Obfuscated_code

Pas tout à fait inutile quand même. Les scripts shell sont encore fort utilisés et certains sont fort complexes. Il suffit de se balader dans /etc/init.d/ pour s’en convaincre.

Oui, et c’est trés bien ce que tu as fait. Rien d’autre à faire à mon avis aussi.
Mais je ne sais pas si on parle à juste titre de référence passée …
Une référence indirecte, c’est ni plus ni moins qu’une évaluation, {!tab_str},
c’est un peu eval non , d’un littéral qui a un sens ?

Je dis pas le contraire je dis que d’une manière générale vouloir tout faire avec un langage c’est se voiler la face et que chercher à recoder linux en *sh c’est vouer à l’écheque.

[quote=“usinagaz”]Mais je ne sais pas si on parle à juste titre de référence passée …
Une référence indirecte, c’est ni plus ni moins qu’une évaluation, {!tab_str},
c’est un peu eval non , d’un littéral qui a un sens ?[/quote]

Il s’agit vraiment de passage par référence car le tableau de départ est modifié si on modifie son nameref dans la fonction. Je parle de ksh. Pour bash, je n’ai pas testé. Trop confus comme solution.

Les chèques en bois? :wink:

C’est bien pourquoi j’appelle ça un exercice de style. Ce n’est rien d’autre que ça. Sans doute pour les mêmes raisons que celles qui te font préférer zsh. Parce qu’entre-nous, les nouvelles fonctionnalités de zsh, n’ont pas plus de raisons d’être que les nameref (références indirectes) de ksh. Elles facilitent la vie du “scripteur” sans plus. Tu n’as pas choisi zsh pour recoder linux non plus je présume? :wink:

J’ai codé 2 ou 3 trucs avec zsh pour voir les différences.

99,9% de mes scripts shell sont posix (modulo les programmes lancés en plus).