208 votes

Passer des tableaux comme paramètres en bash

Comment puis-je passer un tableau comme paramètre à une fonction bash ?

Note : N'ayant pas trouvé de réponse ici sur Stack Overflow, j'ai posté moi-même ma solution quelque peu rudimentaire. Elle ne permet de passer qu'un seul tableau, et celui-ci est le dernier élément de la liste des paramètres. En fait, il ne s'agit pas de passer le tableau, mais une liste de ses éléments, qui sont ré-assemblés en un tableau par la commande called_function() mais ça a marché pour moi. Si quelqu'un connaît une meilleure méthode, n'hésitez pas à l'ajouter ici.

2 votes

Ici vous avez de belles références et des tonnes d'exemples.

18 votes

Errr... Trois votes négatifs sur une question vieille de cinq ans dans la même minute ?

0 votes

Voici une réponse complète : stackoverflow.com/questions/6212219/

232voto

Ken Bertelson Points 864

Vous pouvez passer plusieurs tableaux comme arguments en utilisant quelque chose comme ça :

takes_ary_as_arg()
{
    declare -a argAry1=("${!1}")
    echo "${argAry1[@]}"

    declare -a argAry2=("${!2}")
    echo "${argAry2[@]}"
}
try_with_local_arys()
{
    # array variables could have local scope
    local descTable=(
        "sli4-iread"
        "sli4-iwrite"
        "sli3-iread"
        "sli3-iwrite"
    )
    local optsTable=(
        "--msix  --iread"
        "--msix  --iwrite"
        "--msi   --iread"
        "--msi   --iwrite"
    )
    takes_ary_as_arg descTable[@] optsTable[@]
}
try_with_local_arys

fera écho :

sli4-iread sli4-iwrite sli3-iread sli3-iwrite  
--msix  --iread --msix  --iwrite --msi   --iread --msi   --iwrite

Édition/notes : (d'après les commentaires ci-dessous)

  • descTable y optsTable sont passés comme des noms et sont développés dans la fonction. Ainsi, aucun $ est nécessaire lorsqu'elle est donnée en paramètre.
  • Notez que cela fonctionne même avec descTable etc. étant définis avec local car les locaux sont visibles par les fonctions qu'ils appellent.
  • Le site ! en ${!1} développe la variable arg 1.
  • declare -a rend simplement le tableau indexé explicite, ce n'est pas strictement nécessaire.

14 votes

Une chose à noter est que si le tableau d'origine est clairsemé, le tableau dans la fonction de réception n'aura pas les mêmes indices.

13 votes

C'est génial, mais est-ce que Ken ou quelqu'un d'autre peut expliquer deux ou trois choses qui me laissent perplexe quant à la raison pour laquelle cela fonctionne : 1 - J'aurais pensé que descTable et optsTable auraient dû être préfixés par $ lorsqu'ils sont passés comme arguments de fonction. 2 - Dans la première ligne de "takes...", pourquoi une déclaration de tableau explicite est-elle nécessaire ? 3 - Et que signifie le ! dans l'expression ${!1}, et pourquoi [@] n'est-il pas nécessaire ou même autorisé à cet endroit ? -- Cela fonctionne, et tous ces détails semblent être nécessaires d'après mes tests, mais j'aimerais comprendre pourquoi !

8 votes

1 : descTable et optsTable sont juste passés comme des noms, donc il n'y a pas de $, ils doivent être développés uniquement dans la fonction appelée 2 : je ne suis pas totalement sûr, mais je pense que ce n'est pas vraiment nécessaire 3 : le ! est utilisé parce que les paramètres passés à la fonction doivent être développés deux fois : $1 se développe en "descTable[@]", et cela devrait être développé en "${descTable[@]}". C'est ce que fait la syntaxe ${!1}.

90voto

DevSolar Points 18897

Note : Il s'agit de la solution quelque peu rudimentaire que j'ai postée moi-même, après n'avoir pas trouvé de réponse ici sur Stack Overflow. Elle ne permet de passer qu'un seul tableau, et celui-ci est le dernier élément de la liste des paramètres. En fait, il ne s'agit pas de passer le tableau, mais une liste de ses éléments, qui sont ré-assemblés en un tableau par la fonction called_function(), mais cela a fonctionné pour moi. Un peu plus tard, Ken a posté sa solution, mais j'ai gardé la mienne ici pour une référence "historique".

calling_function()
{
    variable="a"
    array=( "x", "y", "z" )
    called_function "${variable}" "${array[@]}"
}

called_function()
{
    local_variable="${1}"
    shift
    local_array=("${@}")
}

21 votes

Trois ans après les faits, cette réponse - conservée uniquement pour des raisons historiques - a reçu deux votes négatifs en l'espace de quelques jours. Comme tristement habituel sur SO, sans aucune note sur les raisons pour lesquelles les gens pensent que cela est justifié. Notez que cette réponse est antérieure à toutes les autres, et que j'ai accepté la réponse de Ken comme étant la meilleure solution. Je suis parfaitement conscient qu'elle est loin d'être parfaite, mais pour quatre raisons. mois c'était le meilleur disponible sur SO. Pourquoi il devrait être rétrogradé de deux années pourquoi il a pris la deuxième place à la solution parfaite de Ken est au-delà de moi.

0 votes

@geirha : Je vous demanderais de vérifier qui a posté la question, qui a posté cette réponse, et qui a probablement accepté la réponse que vous qualifiez de "mauvaise" ;-) Vous pouvez également vérifier le Note dans la question, qui indique pourquoi cette solution est inférieure à celle de Ken.

2 votes

Je sais que vous avez posé la question, que vous avez écrit cette réponse, et que vous avez accepté la mauvaise réponse. C'est pourquoi je l'ai formulé de cette façon. La raison pour laquelle la réponse acceptée est mauvaise est qu'elle essaie de passer un tableau par référence, ce qui est quelque chose que vous devriez vraiment éviter. De plus, l'exemple mélange plusieurs arguments en une seule chaîne. Si vous avez vraiment besoin de passer des tableaux par référence, bash est le mauvais langage pour commencer. Même avec les nouvelles variables nameref de bash 4.3, vous ne pouvez pas éviter les collisions de noms (référence circulaire).

40voto

TheWizard Points 156

Commentant la solution de Ken Bertelson et répondant à Jan Hettich :

Comment cela fonctionne

le site takes_ary_as_arg descTable[@] optsTable[@] ligne dans try_with_local_arys() la fonction envoie :

  1. Il s'agit en fait de créer une copie de la descTable y optsTable qui sont accessibles à la takes_ary_as_arg fonction.

  2. takes_ary_as_arg() La fonction reçoit descTable[@] y optsTable[@] comme des chaînes de caractères, ce qui signifie $1 == descTable[@] y $2 == optsTable[@] .

  3. au début de takes_ary_as_arg() il utilise la fonction ${!parameter} syntaxe, qui est appelée référence indirecte ou parfois double référence ce qui signifie que au lieu d'utiliser $1 nous utilisons la valeur de l'élément élargi valeur de $1 par exemple :

    baba=booba
    variable=baba
    echo ${variable} # baba
    echo ${!variable} # booba

    de même pour $2 .

  4. en mettant cela dans argAry1=("${!1}") crée argAry1 comme un tableau (les parenthèses qui suivent = ) avec l'extension descTable[@] J'aime écrire ici. argAry1=("${descTable[@]}") directement. le site declare il n'est pas nécessaire.

N.B. : Il convient de mentionner que l'initialisation d'un tableau à l'aide de cette forme de parenthèse initialise le nouveau tableau en fonction de l'option IFS o Séparateur de champ interne qui est par défaut onglet , nouvelle ligne y espace . dans ce cas, puisqu'il a utilisé [@] notation, chaque élément est vu par lui-même comme s'il était cité (contrairement aux [*] ).

Ma réserve à son égard

Sur BASH La portée de la variable locale est la fonction courante et chaque fonction enfant appelée à partir de celle-ci, ce qui se traduit par le fait que takes_ary_as_arg() La fonction "voit" ces descTable[@] y optsTable[@] les tableaux, donc cela fonctionne (voir l'explication ci-dessus).

Dans ce cas, pourquoi ne pas examiner directement ces variables ? C'est comme si on y écrivait :

argAry1=("${descTable[@]}")

Voir l'explication ci-dessus, qui ne fait que copier descTable[@] en fonction des valeurs actuelles du tableau IFS .

En résumé

Il s'agit, en substance, de ne rien passer en valeur - comme d'habitude.

Je tiens également à souligner le commentaire de Dennis Williamson ci-dessus : éparses Les tableaux (tableaux dont toutes les clés sont définies - avec des "trous") ne fonctionneront pas comme prévu - nous perdrions les clés et "condenserions" le tableau.

Ceci étant dit, je vois la valeur de la généralisation, les fonctions peuvent donc obtenir les tableaux (ou les copies) sans connaître les noms :

  • pour ~"copies" : cette technique est assez bonne, il faut juste être conscient que les indices (clés) ont disparu.
  • pour des copies réelles : on peut utiliser un eval pour les clés, par exemple :

    eval local keys=(\${!$1})

et ensuite une boucle les utilisant pour créer une copie. Note : ici ! n'est pas utilisé son évaluation indirecte/double précédente, mais plutôt dans le contexte d'un tableau, il retourne les indices du tableau (clés).

  • et, bien sûr, si nous devions passer descTable y optsTable chaînes de caractères (sans [@] ), nous pourrions utiliser le tableau lui-même (comme par référence) avec eval . pour une fonction générique qui accepte les tableaux.

3 votes

Bonnes explications du mécanisme derrière l'explication de Ken Bertelson. A la question "Dans ce cas, pourquoi ne pas regarder directement ces variables elles-mêmes ?", je répondrai : simplement pour la réutilisation de la fonction. Disons que j'ai besoin d'appeler une fonction avec Array1 puis avec Array2 le fait de passer les noms des tableaux devient pratique.

5voto

TheBonsai Points 3112

La réponse de DevSolar comporte un point que je ne comprends pas (il a peut-être une raison spécifique de le faire, mais je n'en vois pas) : Il définit le tableau à partir des paramètres positionnels élément par élément, de manière itérative.

Une approbation plus facile serait

called_function()
{
  ...
  # do everything like shown by DevSolar
  ...

  # now get a copy of the positional parameters
  local_array=("$@")
  ...
}

1 votes

La raison pour laquelle je ne l'ai pas fait est que je n'ai pas du tout joué avec les tableaux de bash jusqu'à il y a quelques jours. Auparavant, je serais passé à Perl si cela était devenu complexe, une option que je n'ai pas dans mon travail actuel. Merci pour l'astuce !

3voto

Steven Penny Points 18523
function aecho {
  set "$1[$2]"
  echo "${!1}"
}

Exemple

$ foo=(dog cat bird)

$ aecho foo 1
cat

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X