Je ne peux pas exporter un tableau d'un script bash vers un autre script bash comme celui-ci:
export myArray[0]="Hello"
export myArray[1]="World"
Quand j'écris comme ça il n'y a pas de problème:
export myArray=("Hello" "World")
Pour plusieurs raisons, je dois initialiser mon tableau en plusieurs lignes. Avez-vous une solution?
Les variables de tableau ne peuvent pas (encore) être exportées.
Depuis la page de manuel de bash version 4.1.5 sous ubuntu 10.04.
La déclaration suivante de Chet Ramey (mainteneur bash actuel en 2011) est probablement la documentation la plus officielle sur ce "bug":
Il n'y a pas vraiment de bon moyen d'encoder une variable de tableau dans l'environnement.
TL; DR: les tableaux exportables ne sont pas directement pris en charge jusqu'à bash-4.3 inclus, mais vous pouvez (effectivement ) exporter des tableaux de deux manières:
Ou, vous pouvez attendre la sortie de bash-4.3 (en développement/état RC en février 2014, voir ARRAY_EXPORT dans le Changelog). Mise à jour: Cette fonctionnalité est pas activée dans 4.3. Si vous définissez ARRAY_EXPORT
Lors de la génération, la génération échouera. L'auteur a déclaré il n'est pas prévu de terminer cette fonctionnalité.
La première chose à comprendre est que l'environnement bash (plus correctement environnement d'exécution de commande ) est différent du concept POSIX d'un environnement. environnement POSIX est une collection de paires name=value
Non typées, et peut être passée d'un processus à ses enfants de diverses manières (en fait une forme limitée de IPC ).
L'environnement d'exécution bash en est en fait un surensemble, avec des variables typées, des indicateurs en lecture seule et exportables, des tableaux, des fonctions et plus encore. Cela explique en partie pourquoi la sortie de set
(bash builtin) et env
ou printenv
diffère.
Lorsque vous invoquez un autre shell bash vous lancez un nouveau processus, vous perdez un état bash. Cependant, si vous dot-source un script, le script est exécuté dans le même environnement; ou si vous exécutez un sous-shell via ( )
l'environnement est également préservé (car bash bifurque, en préservant son état complet, plutôt que de réinitialiser en utilisant l'environnement de processus).
La limitation référencée dans la réponse de @ lesmana survient parce que l'environnement POSIX est simplement des paires name=value
Sans signification supplémentaire, il n'y a donc pas de moyen convenu d'encoder ou de formater des variables typées, voir ci-dessous pour une bizarrerie bash intéressante concernant les fonctions , et un changement à venir dans bash-4.3(fonctionnalité de tableau proposée abandonnée).
Il existe quelques façons simples de le faire en utilisant declare -p
(Intégré) pour sortir certains de l'environnement bash sous la forme d'un ensemble d'une ou plusieurs instructions declare
qui peuvent être utilisées pour reconstruire le type et la valeur d'un "nom". C'est de base sérialisation , mais avec un peu moins de complexité certaines des autres réponses impliquent. declare -p
Préserve les index des tableaux, les tableaux épars et les citations de valeurs gênantes. Pour la sérialisation simple d'un tableau, vous pouvez simplement vider les valeurs ligne par ligne et utiliser read -a myarray
Pour le restaurer (fonctionne avec les tableaux contigus indexés 0, car read -a
Attribue automatiquement les index).
Ces méthodes ne nécessitent aucune modification du ou des scripts auxquels vous transmettez les tableaux.
declare -p array1 array2 > .bash_arrays # serialise to an intermediate file
bash -c ". .bash_arrays; . otherscript.sh" # source both in the same environment
Les variations du formulaire bash -c "..."
Ci-dessus sont parfois (mal) utilisées dans les crontabs pour définir des variables.
Les alternatives incluent:
declare -p array1 array2 > .bash_arrays # serialise to an intermediate file
BASH_ENV=.bash_arrays otherscript.sh # non-interactive startup script
Ou, en une ligne:
BASH_ENV=<(declare -p array1 array2) otherscript.sh
Le dernier utilise substitution de processus pour passer la sortie de la commande declare
en tant que script rc. (Cette méthode ne fonctionne que dans bash-4.0 ou version ultérieure: les versions antérieures inconditionnellement fstat()
fichiers rc et utilisent la taille retournée à read()
le fichier en une seule fois; a FIFO renvoie une taille de 0 et ne fonctionnera donc pas comme prévu.)
Dans un Shell non interactif (c'est-à-dire un script Shell), le fichier pointé par la variable BASH_ENV
Est provenant automatiquement . Vous devez vous assurer que bash est correctement invoqué, en utilisant éventuellement un Shebang pour appeler explicitement "bash", et non #!/bin/sh
Car bash n'honorera pas BASH_ENV
En mode historique/POSIX.
Si tous vos noms de tableaux ont un préfixe commun, vous pouvez utiliser declare -p ${!myprefix*}
Pour développer une liste d'entre eux, au lieu de les énumérer.
Vous ne devriez probablement pas essayer d'exporter et de réimporter l'environnement entier bash en utilisant cette méthode, certaines variables et tableaux bash spéciaux sont en lecture seule, et il peut y avoir d'autres effets secondaires lors de la modification de spécial variables.
(Vous pouvez également faire quelque chose de légèrement désagréable en sérialisant la définition du tableau en une variable exportable et en utilisant eval
, mais n'encourageons pas l'utilisation de eval
...
$ array=([1]=a [10]="b c")
$ export scalar_array=$(declare -p array)
$ bash # start a new Shell
$ eval $scalar_array
$ declare -p array
declare -a array='([1]="a" [10]="b c")'
)
Comme mentionné ci-dessus, il y a une particularité intéressante: un support spécial pour l'exportation de fonctions via l'environnement:
function myfoo() {
echo foo
}
avec export -f
ou set +a
pour activer ce comportement, entraînera cela dans l'environnement (de processus), visible avec printenv
:
myfoo=() { echo foo
}
La variable est functionname
(ou functioname()
pour la compatibilité descendante) et sa valeur est () { functionbody }
. Lorsqu'un processus bash suivant démarre, il recrée une fonction à partir de chacune de ces variables d'environnement. Si vous regardez dans le fichier source bash-4.2 variables.c
, Vous verrez que les variables commençant par () {
Sont traitées spécialement. (Bien que la création d'une fonction utilisant cette syntaxe avec declare -f
Soit interdite.) Update: Le problème de sécurité " Shellshock" est lié à cette fonctionnalité, contemporaine les systèmes peuvent désactiver l'importation automatique de fonctions depuis l'environnement à titre d'atténuation.
Si vous continuez à lire, vous verrez un code de garde #if 0
(Ou #if ARRAY_EXPORT
) Qui vérifie les variables commençant par ([
Et se terminant par )
, Et un commentaire indiquant " Les variables de tableau ne peuvent pas encore être exportées". La bonne nouvelle est que dans la version de développement actuelle bash-4.3rc2, la possibilité d'exporter des tableaux indexés (non associatifs) est activée. Il est peu probable que cette fonctionnalité soit activée, comme indiqué ci-dessus.
Nous pouvons l'utiliser pour créer une fonction qui restaure toutes les données de tableau requises:
% function sharearray() {
array1=(a b c d)
}
% export -f sharearray
% bash -c 'sharearray; echo ${array1[*]}'
Donc, comme pour l'approche précédente, appelez le script enfant avec:
bash -c "sharearray; . otherscript.sh"
Ou, vous pouvez invoquer conditionnellement la fonction sharearray
dans le script enfant en ajoutant à un moment approprié:
[ "`type -t sharearray`" = "function" ] && sharearray
Notez qu'il n'y a pas de declare -a
Dans la fonction sharearray
, si vous faites cela, le tableau est implicitement local à la fonction, ce qui n'est pas ce qui est souhaité. bash-4.2 prend en charge declare -g
qui rend explicitement une variable globale, afin que (declare -ga
) puisse être utilisé alors. (Puisque les tableaux associatifs nécessitentdeclare -A
Vous ne pourrez pas utiliser cette méthode pour les tableaux associatifs avant bash-4.2.) Le GNU = parallel
la documentation a des variations utiles sur cette méthode, voir la discussion de --env
dans la page de manuel .
Votre question, telle qu'elle est formulée, indique également que vous pouvez avoir des problèmes avec export
lui-même. Vous pouvez exporter un nom après l'avoir créé ou modifié. "exportable" est un indicateur ou une propriété d'une variable, pour plus de commodité, vous pouvez également définir et exporter dans une seule instruction. Jusqu'à bash-4.2 export
n'attend qu'un nom, soit une simple variable (scalaire) soit un nom de fonction sont pris en charge.
Même si vous pouviez (à l'avenir) exporter des tableaux, l'exportation d'index sélectionnés (une tranche) peut ne pas être prise en charge (bien que, comme les tableaux sont rares, il n'y a aucune raison pour que cela ne soit pas autorisé). Bien que bash supporte également la syntaxe declare -a name[0]
, L'indice est ignoré et "nom" est simplement un tableau indexé normal.
Bon sang. Je ne sais pas pourquoi les autres réponses ont rendu cela si compliqué. Bash a presque un support intégré pour cela.
Dans le script d'exportation:
myArray=( ' foo"bar ' $'\n''\nbaz)' ) # an array with two nasty elements
myArray="${myArray[@]@Q}" ./importing_script.sh
(Remarque: les guillemets doubles sont nécessaires pour une gestion correcte des espaces blancs dans les éléments du tableau.)
À l'entrée de importing_script.sh
, la valeur de la variable d'environnement myArray
comprend ces 26 octets exacts:
' foo"bar ' $'\n\\nbaz)'
Ensuite, ce qui suit reconstituera le tableau:
eval "myArray=( ${myArray} )"
ATTENTION! Ne pas eval
comme ça si vous ne pouvez pas faire confiance à la source de la variable d'environnement myArray
. Cette astuce présente la vulnérabilité "Little Bobby Tables" . Imaginez que quelqu'un définisse la valeur de myArray
sur ) ; rm -rf / #
.
Comme l'a signalé lesmana, vous ne pouvez pas exporter de tableaux. Vous devez donc les sérialiser avant de traverser l'environnement. Cette sérialisation est également utile à d'autres endroits où ne tient qu'une chaîne (su -c 'chaîne', ssh Hôte 'chaîne'). Le moyen le plus court de le faire est d'abuser de "getopt"
# preserve_array(arguments). return in _RET a string that can be expanded
# later to recreate positional arguments. They can be restored with:
# eval set -- "$_RET"
preserve_array() {
_RET=$(getopt --Shell sh --options "" -- -- "$@") && _RET=${_RET# --}
}
# restore_array(name, payload)
restore_array() {
local name="$1" payload="$2"
eval set -- "$payload"
eval "unset $name && $name=("\$@")"
}
Utilisez-le comme ceci:
foo=("1: &&& - *" "2: two" "3: %# abc" )
preserve_array "${foo[@]}"
foo_stuffed=${_RET}
restore_array newfoo "$foo_stuffed"
for elem in "${newfoo[@]}"; do echo "$elem"; done
## output:
# 1: &&& - *
# 2: two
# 3: %# abc
Cela ne concerne pas les tableaux non définis/clairsemés. Vous pourrez peut-être réduire les 2 appels "eval" dans restore_array.
vous (salut!) pouvez l'utiliser, vous n'avez pas besoin d'écrire un fichier, pour ubuntu 12.04, bash 4.2.24
En outre, votre tableau de plusieurs lignes peut être exporté.
cat >> exportArray.sh
function FUNCarrayRestore() {
local l_arrayName=$1
local l_exportedArrayName=${l_arrayName}_exportedArray
# if set, recover its value to array
if eval '[[ -n ${'$l_exportedArrayName'+dummy} ]]'; then
eval $l_arrayName'='`eval 'echo $'$l_exportedArrayName` #do not put export here!
fi
}
export -f FUNCarrayRestore
function FUNCarrayFakeExport() {
local l_arrayName=$1
local l_exportedArrayName=${l_arrayName}_exportedArray
# prepare to be shown with export -p
eval 'export '$l_arrayName
# collect exportable array in string mode
local l_export=`export -p \
|grep "^declare -ax $l_arrayName=" \
|sed 's"^declare -ax '$l_arrayName'"export '$l_exportedArrayName'"'`
# creates exportable non array variable (at child Shell)
eval "$l_export"
}
export -f FUNCarrayFakeExport
testez cet exemple sur terminal bash (fonctionne avec bash 4.2.24):
source exportArray.sh
list=(a b c)
FUNCarrayFakeExport list
bash
echo ${list[@]} #empty :(
FUNCarrayRestore list
echo ${list[@]} #profit! :D
Je peux l'améliorer ici
PS: si quelqu'un efface/améliore/makeItRunFaster je voudrais savoir/voir, merci! :RÉ
Beaucoup grâce à @ stéphane-chazelas qui a signalé tous les problèmes avec mes précédentes tentatives, cela semble maintenant fonctionner pour sérialiser un tableau en stdout ou en variable.
Cette technique n'analyse pas l'entrée en shell (contrairement à declare -a
/declare -p
) et est ainsi protégé contre l'insertion malveillante de métacaractères dans le texte sérialisé.
Remarque: les sauts de ligne ne sont pas échappés, car read
supprime le \<newlines>
paire de caractères, donc -d ...
doit à la place être passé en lecture, puis les retours à la ligne non échappés sont conservés.
Tout cela est géré dans la fonction unserialise
.
Deux caractères magiques sont utilisés, le séparateur de champ et le séparateur d'enregistrement (afin que plusieurs tableaux puissent être sérialisés dans le même flux).
Ces caractères peuvent être définis comme FS
et RS
mais aucun ne peut être défini comme newline
car une nouvelle ligne échappée est supprimée par read
.
Le caractère d'échappement doit être \
la barre oblique inverse, car c'est ce qui est utilisé par read
pour éviter que le caractère ne soit reconnu comme IFS
.
serialise
sérialisera "$@"
à stdout, serialise_to
va sérialiser vers la variable nommée dans $1
serialise() {
set -- "${@//\\/\\\\}" # \
set -- "${@//${FS:-;}/\\${FS:-;}}" # ; - our field separator
set -- "${@//${RS:-:}/\\${RS:-:}}" # ; - our record separator
local IFS="${FS:-;}"
printf ${SERIALIZE_TARGET:+-v"$SERIALIZE_TARGET"} "%s" "$*${RS:-:}"
}
serialise_to() {
SERIALIZE_TARGET="$1" serialise "${@:2}"
}
unserialise() {
local IFS="${FS:-;}"
if test -n "$2"
then read -d "${RS:-:}" -a "$1" <<<"${*:2}"
else read -d "${RS:-:}" -a "$1"
fi
}
et désérialiser avec:
unserialise data # read from stdin
ou
unserialise data "$serialised_data" # from args
par exemple.
$ serialise "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party Party Party:
(sans retour à la ligne)
relisez-le:
$ serialise_to s "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
$ unserialise array "$s"
$ echo "${array[@]/#/$'\n'}"
Now is the time
For all good men
To drink $drink
At the `party`
Party Party Party
ou
unserialise array # read from stdin
read
de Bash respecte le caractère d'échappement \
(sauf si vous passez l'indicateur -r) pour supprimer la signification spéciale des caractères tels que la séparation des champs de saisie ou la délimitation des lignes.
Si vous voulez sérialiser un tableau au lieu d'une simple liste d'arguments, passez simplement votre tableau comme liste d'arguments:
serialise_array "${my_array[@]}"
Vous pouvez utiliser unserialise
dans une boucle comme vous le feriez read
car il s'agit simplement d'une lecture encapsulée - mais n'oubliez pas que le flux n'est pas séparé par des sauts de ligne:
while unserialise array
do ...
done
Pour les tableaux avec des valeurs sans espaces, j'ai utilisé un ensemble simple de fonctions pour parcourir chaque élément du tableau et concaténer le tableau:
_arrayToStr(){
array=($@)
arrayString=""
for (( i=0; i<${#array[@]}; i++ )); do
if [[ $i == 0 ]]; then
arrayString="\"${array[i]}\""
else
arrayString="${arrayString} \"${array[i]}\""
fi
done
export arrayString="(${arrayString})"
}
_strToArray(){
str=$1
array=${str//\"/}
array=(${array//[()]/""})
export array=${array[@]}
}
La première fonction transforme le tableau en chaîne en ajoutant les parenthèses ouvrantes et fermantes et en échappant à tous les guillemets doubles. La deuxième fonction supprimera les guillemets et les parenthèses et les placera dans un tableau factice.
Pour exporter le tableau, vous devez passer tous les éléments du tableau d'origine:
array=(foo bar)
_arrayToStr ${array[@]}
À ce stade, le tableau a été exporté dans la valeur $ arrayString. Pour importer le tableau dans le fichier de destination, renommez le tableau et effectuez la conversion opposée:
_strToArray "$arrayName"
newArray=(${array[@]})
J'étais en train de modifier un autre post et j'ai fait une erreur. Augh. Quoi qu'il en soit, cela pourrait peut-être aider? https://stackoverflow.com/a/11944320/1594168
Notez que parce que le format de tableau de Shell n'est pas documenté sur bash ou tout autre côté de Shell, il est très difficile de renvoyer un tableau de Shell de manière indépendante de la plateforme. Vous devez vérifier la version et créer également un script simple qui concatrie tous les tableaux Shell dans un fichier dans lequel d'autres processus peuvent être résolus.
Cependant, si vous connaissez le nom du tableau que vous souhaitez ramener à la maison , il existe un moyen, tout en étant un peu sale.
Disons que j'ai
MyAry[42]="whatever-stuff";
MyAry[55]="foo";
MyAry[99]="bar";
Je veux donc le ramener à la maison
name_of_child=MyAry
take_me_home="`declare -p ${name_of_child}`";
export take_me_home="${take_me_home/#declare -a ${name_of_child}=/}"
On peut le voir s'exporter, en vérifiant à partir d'un sous-processus
echo ""|awk '{print "from awk =["ENVIRON["take_me_home"]"]"; }'
Résultat :
from awk =['([42]="whatever-stuff" [55]="foo" [99]="bar")']
Si nous devons absolument, utilisez le var env pour le vider.
env > some_tmp_file
Alors
Avant d'exécuter un autre script,
# This is the magic that does it all
source some_tmp_file
Basé sur @ mr.spuratic use of BASH_ENV
, Ici je tunnel $@
À travers script -f -c
script -c <command> <logfile>
Peut être utilisé pour exécuter une commande dans un autre pty (et groupe de processus) mais il ne peut pas passer d'arguments structurés à <command>
.
Au lieu de cela, <command>
Est une simple chaîne devant servir d'argument à l'appel de bibliothèque system
.
J'ai besoin de tunneler $@
Du bash externe dans $@
Du bash invoqué par le script.
Comme declare -p
Ne peut pas prendre @
, J'utilise ici la variable magic bash _
(Avec une première valeur de tableau factice car elle sera écrasée par bash). Cela m'évite de piétiner toutes les variables importantes:
Preuve de concept: BASH_ENV=<( declare -a _=("" "$@") && declare -p _ ) bash -c 'set -- "${_[@]:1}" && echo "$@"'
"Mais," dites-vous, "vous passez des arguments à bash - et en effet je le suis, mais ce sont une simple chaîne de caractères connus. Voici l'utilisation par script
Shell=/bin/bash BASH_ENV=<( declare -a _=("" "$@") && declare -p _ && echo 'set -- "${_[@]:1}"') script -f -c 'echo "$@"' /tmp/logfile
ce qui me donne cette fonction wrapper in_pty
:
in_pty() { Shell=/bin/bash BASH_ENV=<( declare -a _=("" "$@") && declare -p _ && echo 'set -- "${_[@]:1}"') script -f -c 'echo "$@"' /tmp/logfile }
ou ce wrapper sans fonction en tant que chaîne composable pour Makefiles:
in_pty=bash -c 'Shell=/bin/bash BASH_ENV=<( declare -a _=("" "$$@") && declare -p _ && echo '"'"'set -- "$${_[@]:1}"'"'"') script -qfc '"'"'"$$@"'"'"' /tmp/logfile' --
...
$(in_pty) test --verbose $@ $^