Comment puis-je passer un tableau en tant que paramètre à une fonction bash?
Remarque: après n'avoir pas trouvé de réponse ici sur Stack Overflow, j'ai publié moi-même ma solution un peu brute. Il ne permet qu'un seul tableau soit passé, et c'est le dernier élément de la liste de paramètres. En fait, il ne passe pas du tout le tableau, mais une liste de ses éléments, qui sont réassemblés dans un tableau par called_function()
, mais cela a fonctionné pour moi. Si quelqu'un connaît un meilleur moyen, n'hésitez pas à l'ajouter ici.
Réponses:
Vous pouvez passer plusieurs tableaux comme arguments en utilisant quelque chose comme ceci:
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:
Modifier / notes: (à partir des commentaires ci-dessous)
descTable
etoptsTable
sont passés en tant que noms et sont développés dans la fonction. Ainsi, aucun$
n'est nécessaire lorsqu'il est donné comme paramètres.descTable
etc est défini aveclocal
, car les locaux sont visibles par les fonctions qu'ils appellent.!
in${!1}
développe la variable arg 1.declare -a
rend simplement le tableau indexé explicite, ce n'est pas strictement nécessaire.la source
Remarque: C'est la solution un peu brute que j'ai publiée moi-même, après n'avoir pas trouvé de réponse ici sur Stack Overflow. Il ne permet qu'un seul tableau soit passé, et c'est le dernier élément de la liste de paramètres. En fait, il ne passe pas du tout le tableau, mais une liste de ses éléments, qui sont réassemblés dans un tableau par 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 référence "historique".
calling_function() { variable="a" array=( "x", "y", "z" ) called_function "${variable}" "${array[@]}" } called_function() { local_variable="${1}" shift local_array=("${@}") }
la source
called_function "${#array[@]}" "${array[@]}" "${#array2[@]}" "${array2[@]}"
etc ... toujours avec des restrictions évidentes, mais vraiment, mieux vaut résoudre le problème d'une manière prise en charge par la langue, plutôt que d'essayer de plier la langue pour qu'elle fonctionne comme vous en avez l'habitude dans d'autres langues.Commentant la solution de Ken Bertelson et répondant à Jan Hettich:
Comment ça fonctionne
la
takes_ary_as_arg descTable[@] optsTable[@]
ligne entry_with_local_arys()
fonction envoie:descTable
etoptsTable
qui sont accessibles à latakes_ary_as_arg
fonction.takes_ary_as_arg()
fonction reçoitdescTable[@]
etoptsTable[@]
sous forme de chaînes, cela signifie$1 == descTable[@]
et$2 == optsTable[@]
.au début de la
takes_ary_as_arg()
fonction, il utilise la${!parameter}
syntaxe, qui est appelée référence indirecte ou parfois double référence , cela signifie qu'au lieu d'utiliser$1
la valeur de, nous utilisons la valeur de la valeur développée de$1
, exemple:baba=booba variable=baba echo ${variable} # baba echo ${!variable} # booba
de même pour
$2
.argAry1=("${!1}")
créeargAry1
comme un tableau (les crochets suivants=
) avec le développédescTable[@]
, tout comme écrire iciargAry1=("${descTable[@]}")
directement. ledeclare
il n'est pas nécessaire.NB: Il est à noter que l'initialisation du tableau à l'aide de cette forme de parenthèse initialise le nouveau tableau en fonction du
IFS
ou du séparateur de champ interne qui est par défaut tab , nouvelle ligne et espace . dans ce cas, puisqu'il utilise la[@]
notation, chaque élément est vu par lui-même comme s'il était cité (contrairement à[*]
).Ma réservation avec
Dans
BASH
, la portée de la variable locale est la fonction courante et chaque fonction enfant appelée à partir de celle-ci, cela se traduit par le fait que latakes_ary_as_arg()
fonction "voit" ceux-cidescTable[@]
et lesoptsTable[@]
tableaux, donc cela fonctionne (voir l'explication ci-dessus).Dans ce cas, pourquoi ne pas examiner directement ces variables elles-mêmes? C'est comme écrire là-bas:
argAry1=("${descTable[@]}")
Voir l'explication ci-dessus, qui copie simplement
descTable[@]
les valeurs du tableau en fonction du courantIFS
.En résumé
Je tiens également à souligner le commentaire de Dennis Williamson ci-dessus: les tableaux clairsemés (tableaux sans toutes les clés définies - avec des "trous" en eux) ne fonctionneront pas comme prévu - nous perdrions les clés et "condenserions" le tableau.
Cela étant dit, je vois la valeur de la généralisation, les fonctions peuvent donc obtenir les tableaux (ou copies) sans connaître les noms:
pour les copies réelles: on peut utiliser un eval pour les clés, par exemple:
eval local keys=(\${!$1})
puis une boucle les utilisant pour créer une copie. Remarque: ici
!
n'est pas utilisé lors de l'évaluation indirecte / double précédente, mais plutôt dans le contexte d'un tableau, il renvoie les indices du tableau (clés).descTable
et desoptsTable
chaînes (sans[@]
), nous pourrions utiliser le tableau lui-même (comme par référence) aveceval
. pour une fonction générique qui accepte des tableaux.la source
Array1
, puis avecArray2
, passer les noms de tableau devient pratique.Le problème de base ici est que le (s) développeur (s) de bash qui a conçu / implémenté des tableaux ont vraiment vissé le chien. Ils ont décidé que
${array}
c'était juste une main courte${array[0]}
, ce qui était une grave erreur. Surtout lorsque vous considérez que cela${array[0]}
n'a pas de sens et que vous évaluez la chaîne vide si le type de tableau est associatif.L'affectation d'un tableau prend la forme
array=(value1 ... valueN)
où value a la syntaxe[subscript]=string
, affectant ainsi une valeur directement à un index particulier du tableau. Cela fait qu'il peut y avoir deux types de tableaux, indexés numériquement et indexés par hachage (appelés tableaux associatifs en langage bash). Cela permet également de créer des tableaux à indexation numérique clairsemés. Laisser la[subscript]=
partie est abrégé pour un tableau indexé numériquement, en commençant par l'indice ordinal de 0 et en incrémentant à chaque nouvelle valeur dans l'instruction d'affectation.Par conséquent,
${array}
devrait évaluer le tableau entier , les index et tout. Il doit être évalué à l'inverse de l'instruction d'affectation. Toute majeure de troisième année en CS devrait le savoir. Dans ce cas, ce code fonctionnerait exactement comme vous pouvez vous y attendre:declare -A foo bar foo=${bar}
Ensuite, passer des tableaux par valeur aux fonctions et assigner un tableau à un autre fonctionnerait comme le reste de la syntaxe du shell. Mais comme ils n'ont pas fait cela correctement, l'opérateur d'affectation
=
ne fonctionne pas pour les tableaux, et les tableaux ne peuvent pas être passés par valeur à des fonctions ou à des sous-shell ou à une sortie en général (echo ${array}
) sans code pour tout mâcher.Donc, si cela avait été fait correctement, l'exemple suivant montrerait comment l'utilité des tableaux dans bash pourrait être nettement meilleure:
simple=(first=one second=2 third=3) echo ${simple}
la sortie résultante doit être:
Ensuite, les tableaux pourraient utiliser l'opérateur d'affectation et être passés par valeur aux fonctions et même à d'autres scripts shell. Facilement stocké en sortant dans un fichier, et facilement chargé d'un fichier dans un script.
declare -A foo read foo <file
Hélas, nous avons été déçus par une équipe de développement de bash par ailleurs superlative.
En tant que tel, pour passer un tableau à une fonction, il n'y a vraiment qu'une seule option, et c'est d'utiliser la fonctionnalité nameref:
function funky() { local -n ARR ARR=$1 echo "indexes: ${!ARR[@]}" echo "values: ${ARR[@]}" } declare -A HASH HASH=([foo]=bar [zoom]=fast) funky HASH # notice that I'm just passing the word 'HASH' to the function
entraînera la sortie suivante:
Comme cela passe par référence, vous pouvez également attribuer au tableau dans la fonction. Oui, le tableau référencé doit avoir une portée globale, mais cela ne devrait pas être trop gros, étant donné qu'il s'agit d'un script shell. Pour passer un tableau indexé associatif ou clairsemé par valeur à une fonction, il faut lancer tous les index et les valeurs sur la liste d'arguments (pas trop utile s'il s'agit d'un grand tableau) sous forme de chaînes simples comme celle-ci:
funky "${!array[*]}" "${array[*]}"
puis écrire un tas de code à l'intérieur de la fonction pour réassembler le tableau.
la source
local -n
est meilleure et plus à jour que la réponse acceptée. Cette solution fonctionnera également pour une variable de tout type. L'exemple répertorié dans cette réponse peut être abrégé enlocal -n ARR=${1}
. Cependant, l'-n
option pourlocal
/declare
n'est disponible que dans les versions 4.3 et supérieures de Bash.funky ARR
), le shell donnera un avertissementcircular name reference
, car fondamentalement la fonction essaiera de fairelocal -n ARR=ARR
. Bonne discussion sur ce sujet.La réponse de DevSolar a un point que je ne comprends pas (peut-être qu'il a une raison spécifique de le faire, mais je ne peux pas penser à une seule): il définit le tableau à partir des paramètres de position élément par élément, itératif.
Une approche plus facile serait
called_function() { ... # do everything like shown by DevSolar ... # now get a copy of the positional parameters local_array=("$@") ... }
la source
Un moyen simple de passer plusieurs tableaux en tant que paramètre consiste à utiliser une chaîne séparée par des caractères. Vous pouvez appeler votre script comme ceci:
./myScript.sh "value1;value2;value3" "somethingElse" "value4;value5" "anotherOne"
Ensuite, vous pouvez l'extraire dans votre code comme ceci:
myArray=$1 IFS=';' read -a myArray <<< "$myArray" myOtherArray=$3 IFS=';' read -a myOtherArray <<< "$myOtherArray"
De cette façon, vous pouvez réellement passer plusieurs tableaux en tant que paramètres et il n'est pas nécessaire que ce soit les derniers paramètres.
la source
function aecho { set "$1[$2]" echo "${!1}" }
Exemple
la source
Celui-ci fonctionne même avec des espaces:
format="\t%2s - %s\n" function doAction { local_array=("$@") for (( i = 0 ; i < ${#local_array[@]} ; i++ )) do printf "${format}" $i "${local_array[$i]}" done echo -n "Choose: " option="" read -n1 option echo ${local_array[option]} return } #the call: doAction "${tools[@]}"
la source
Avec quelques astuces, vous pouvez réellement passer des paramètres nommés aux fonctions, avec des tableaux.
La méthode que j'ai développée vous permet d'accéder aux paramètres passés à une fonction comme celle-ci:
testPassingParams() { @var hello l=4 @array anArrayWithFourElements l=2 @array anotherArrayWithTwo @var anotherSingle @reference table # references only work in bash >=4.3 @params anArrayOfVariedSize test "$hello" = "$1" && echo correct # test "${anArrayWithFourElements[0]}" = "$2" && echo correct test "${anArrayWithFourElements[1]}" = "$3" && echo correct test "${anArrayWithFourElements[2]}" = "$4" && echo correct # etc... # test "${anotherArrayWithTwo[0]}" = "$6" && echo correct test "${anotherArrayWithTwo[1]}" = "$7" && echo correct # test "$anotherSingle" = "$8" && echo correct # test "${table[test]}" = "works" table[inside]="adding a new value" # # I'm using * just in this example: test "${anArrayOfVariedSize[*]}" = "${*:10}" && echo correct } fourElements=( a1 a2 "a3 with spaces" a4 ) twoElements=( b1 b2 ) declare -A assocArray assocArray[test]="works" testPassingParams "first" "${fourElements[@]}" "${twoElements[@]}" "single with spaces" assocArray "and more... " "even more..." test "${assocArray[inside]}" = "adding a new value"
En d'autres termes, non seulement vous pouvez appeler vos paramètres par leurs noms (ce qui constitue un noyau plus lisible), mais vous pouvez en fait passer des tableaux (et des références à des variables - cette fonctionnalité ne fonctionne que dans bash 4.3 cependant)! De plus, les variables mappées sont toutes dans la portée locale, tout comme $ 1 (et autres).
Le code qui fait ce travail est assez léger et fonctionne à la fois dans bash 3 et bash 4 (ce sont les seules versions avec lesquelles je l'ai testé). Si vous êtes intéressé par d'autres astuces comme celle-ci qui rendent le développement avec bash beaucoup plus agréable et plus facile, vous pouvez jeter un œil à mon Bash Infinity Framework , le code ci-dessous a été développé à cet effet.
Function.AssignParamLocally() { local commandWithArgs=( $1 ) local command="${commandWithArgs[0]}" shift if [[ "$command" == "trap" || "$command" == "l="* || "$command" == "_type="* ]] then paramNo+=-1 return 0 fi if [[ "$command" != "local" ]] then assignNormalCodeStarted=true fi local varDeclaration="${commandWithArgs[1]}" if [[ $varDeclaration == '-n' ]] then varDeclaration="${commandWithArgs[2]}" fi local varName="${varDeclaration%%=*}" # var value is only important if making an object later on from it local varValue="${varDeclaration#*=}" if [[ ! -z $assignVarType ]] then local previousParamNo=$(expr $paramNo - 1) if [[ "$assignVarType" == "array" ]] then # passing array: execute="$assignVarName=( \"\${@:$previousParamNo:$assignArrLength}\" )" eval "$execute" paramNo+=$(expr $assignArrLength - 1) unset assignArrLength elif [[ "$assignVarType" == "params" ]] then execute="$assignVarName=( \"\${@:$previousParamNo}\" )" eval "$execute" elif [[ "$assignVarType" == "reference" ]] then execute="$assignVarName=\"\$$previousParamNo\"" eval "$execute" elif [[ ! -z "${!previousParamNo}" ]] then execute="$assignVarName=\"\$$previousParamNo\"" eval "$execute" fi fi assignVarType="$__capture_type" assignVarName="$varName" assignArrLength="$__capture_arrLength" } Function.CaptureParams() { __capture_type="$_type" __capture_arrLength="$l" } alias @trapAssign='Function.CaptureParams; trap "declare -i \"paramNo+=1\"; Function.AssignParamLocally \"\$BASH_COMMAND\" \"\$@\"; [[ \$assignNormalCodeStarted = true ]] && trap - DEBUG && unset assignVarType && unset assignVarName && unset assignNormalCodeStarted && unset paramNo" DEBUG; ' alias @param='@trapAssign local' alias @reference='_type=reference @trapAssign local -n' alias @var='_type=var @param' alias @params='_type=params @param' alias @array='_type=array @param'
la source
Juste pour ajouter à la réponse acceptée, car j'ai trouvé que cela ne fonctionne pas bien si le contenu du tableau ressemble à quelque chose comme:
RUN_COMMANDS=( "command1 param1... paramN" "command2 param1... paramN" )
Dans ce cas, chaque membre du tableau est divisé, donc le tableau que la fonction voit est équivalent à:
RUN_COMMANDS=( "command1" "param1" ... "command2" ... )
Pour que ce cas fonctionne, la façon dont j'ai trouvé est de passer le nom de la variable à la fonction, puis d'utiliser eval:
function () { eval 'COMMANDS=( "${'"$1"'[@]}" )' for COMMAND in "${COMMANDS[@]}"; do echo $COMMAND done } function RUN_COMMANDS
Juste mon 2 ©
la source
Aussi laid soit-il, voici une solution de contournement qui fonctionne tant que vous ne passez pas un tableau explicitement, mais une variable correspondant à un tableau:
function passarray() { eval array_internally=("$(echo '${'$1'[@]}')") # access array now via array_internally echo "${array_internally[@]}" #... } array=(0 1 2 3 4 5) passarray array # echo's (0 1 2 3 4 5) as expected
Je suis sûr que quelqu'un peut proposer une implémentation plus claire de l'idée, mais j'ai trouvé que c'était une meilleure solution que de passer un tableau en tant
"{array[@]"}
que puis d'y accéder en internearray_inside=("$@")
. Cela devient compliqué lorsqu'il y a d'autresgetopts
paramètres / positionnels . Dans ces cas, j'ai d'abord dû déterminer, puis supprimer les paramètres non associés au tableau en utilisant une combinaison deshift
suppression d'élément de tableau.Une perspective puriste considère probablement cette approche comme une violation du langage, mais pragmatiquement parlant, cette approche m'a évité beaucoup de chagrin. Sur un sujet connexe, j'utilise également
eval
pour attribuer un tableau construit en interne à une variable nommée en fonction d'un paramètre quetarget_varname
je passe à la fonction:eval $target_varname=$"(${array_inside[@]})"
J'espère que cela aide quelqu'un.
la source
Condition : fonction pour trouver une chaîne dans un tableau.
Il s'agit d'une légère simplification de la solution de DevSolar en ce qu'elle utilise les arguments passés plutôt que de les copier.
myarray=('foobar' 'foxbat') function isInArray() { local item=$1 shift for one in $@; do if [ $one = $item ]; then return 0 # found fi done return 1 # not found } var='foobar' if isInArray $var ${myarray[@]}; then echo "$var found in array" else echo "$var not found in array" fi
la source
Ma réponse courte est:
${test_array[*]}
et${test_array2[*]}
doit être entouré de "", sinon vous échouerez.la source