Puis-je «exporter» des fonctions dans bash?

81
source some_file

un_fichier:

doit ()
{
  echo doit $1
}
export TEST=true

Si je source un fichier, la fonction "doit" et la variable TEST sont disponibles sur la ligne de commande. Mais en exécutant ce script:

script.sh:

#/bin/sh
echo $TEST
doit test2

Renverra la valeur de TEST, mais générera une erreur concernant la fonction inconnue "doit".

Puis-je également "exporter" la fonction ou dois-je utiliser source_file dans script.sh pour pouvoir utiliser la fonction à cet endroit?

Nils
la source
2
résumant les réponses ci-dessous (enzotib est correct, en supposant que vous puissiez utiliser bash, comme l'indique la question): changez #!/bin/shpour #!/bin/bashet juste après doit() {...}export -f doit
michael
Pour mémoire: Cette solution fonctionnera généralement lorsque vous utiliserez #!/bin/shaussi, mais il est recommandé de l’utiliser #!/bin/bashafin d’éviter les problèmes lorsque le shell par défaut n’est pas bash.
Nagel
stackoverflow.com/questions/1885871/…
Ciro Santilli a dévoilé

Réponses:

120

Dans Bash, vous pouvez exporter les définitions de fonctions vers un sous-shell avec

export -f function_name

Par exemple, vous pouvez essayer cet exemple simple:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Ensuite, si vous appelez, ./script1vous verrez la sortie Hello! .

enzotib
la source
16

"Exporter" une fonction en utilisant export -fcrée une variable d'environnement avec le corps de la fonction. Considérons cet exemple:

$ fn(){ echo \'\"\ \ \$; }
$ export -f fn
$ sh -c printenv\ fn
() {  echo \'\"\ \ \$
}

Cela signifie que seul le shell (juste Bash?) Pourra accepter la fonction. Vous pouvez également définir la fonction vous-même, car le Bash ne considère que les envvars commençant par () {:

$ fn2='() { echo Hi;}' sh -c fn2
Hi
$ fn3='() {' sh -c :
sh: fn3: line 1: syntax error: unexpected end of file
sh: error importing function definition for `fn3'

Si vous devez "exporter" cette variable sur SSH, vous avez vraiment besoin de la fonction sous forme de chaîne. Cela peut être fait avec l'option d'impression ( -p) pour les fonctions ( -f) de la fonction declareintégrée:

$ declare -pf fn
fn () 
{ 
    echo \'\"\ \ \$
}

Ceci est très utile si vous avez du code plus complexe qui doit être exécuté sur SSH. Considérez le script fictif suivant:

#!/bin/bash
remote_main() {
   local dest="$HOME/destination"

   tar xzv -C "$dest"
   chgrp -R www-data "$dest"
   # Ensure that newly written files have the 'www-data' group too
   find "$dest" -type d -exec chmod g+s {} \;
}
tar cz files/ | ssh user@host "$(declare -pf remote_main); remote_main"
Lekensteyn
la source
fn2='() { echo Hi;}' sh -c fn2N'a pas travaillé pour moi. Sur Linux avec shbash v5.0.7, je l’ai sh: fn2: command not found. Sur Ubuntu avec shdash v0.2.3, je l'ai sh: 1: fn2: not found. Quel shshell avez-vous utilisé?
Socowi le
Je pense que votre réponse est fausse. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'n'imprime rien pour moi, il fest donc clair que ce n'est pas exporté en tant que chaîne simple. J'ai testé avec les deux combinaisons mentionnées ci-dessus.
Socowi le
Et même si la fonction a été exportée en tant que chaîne, pourquoi devrait-elle shexécuter cette chaîne en tant que fonction? Dans votre exemple, fn2='() { echo Hi;}' sh -c fn2la commande fn2donnée à sh est littéralement la chaîne "fn2". shdevrait rechercher une telle commande dans son PATH mais ne devrait pas rechercher s'il existe une variable $fn2, développer cette variable et exécuter sa valeur en tant que fonction - cela me semble être un problème de sécurité majeur. edit: Je pense que c'est ça! Votre comportement affiché était-il un bogue de sécurité connu sous le nom de ShellShock / Bashdoor ?
Socowi le
Avec Bash 5.0.7 (Arch Linux), il semble qu'un préfixe soit ajouté au début. Cela a probablement été fait en réponse à ShellShock. Exemple: fn(){ echo foo; }; export -f fn; env | grep fooSortiesBASH_FUNC_fn%%=() { echo foo
Lekensteyn Le
7

S'appuyant sur la réponse de @ Lekensteyn ...

Si vous l'utilisez declare -pf, toutes les fonctions précédemment définies dans le shell actuel seront sorties vers STDOUT.

À ce stade, vous pouvez rediriger STDOUT où vous le souhaitez et, dans la pratique, farcir les fonctions précédemment définies où vous le souhaitez.

La réponse suivante va les fourrer dans une variable. Ensuite, nous renvoyons cette variable à l'appel de la fonction que nous voulons exécuter dans le nouveau shell créé en tant que nouvel utilisateur. Nous faisons cela en utilisant sudoavec le commutateur -u(aka. user) Et en exécutant simplement Bash (qui recevra le STDOUT acheminé comme entrée à exécuter).

Comme nous savons que nous passons d’un shell Bash à un shell Bash, nous savons que Bash interprétera correctement les fonctions définies par les shell précédents. La syntaxe devrait être correcte tant que nous passons d'un shell Bash de la même version à un nouveau shell Bash de la même version.

YMMV si vous vous déplacez entre différents shells ou entre des systèmes pouvant avoir différentes versions de Bash.

#!/bin/bash
foo() {
  echo "hello from `whoami`"
}

FUNCTIONS=`declare -pf`; echo "$FUNCTIONS ; foo" | sudo -u otheruser bash
# $./test.sh
# hello from otheruser
Jesse
la source
5

Vous ne pouvez pas exporter de fonctions, pas comme vous le décrivez. Le shell ne chargera le ~/.bashrcfichier qu'au début d'un shell interactif (recherchez "Invocation" dans la page de manuel bash ).

Ce que vous pouvez faire est de créer une "bibliothèque" qui est chargée lorsque vous démarrez le programme:

source "$HOME/lib/somefile"

Et placez-y vos fonctions et paramètres non interactifs.

Arcege
la source
Je dois donc soit démarrer le sous-shell avec le paramètre "login" (pour analyser ~ / .profile), soit utiliser le fichier source.
Nils
En regardant un peu plus près, sur les shells non interactifs, vous pouvez définir BASH_ENVla variable d'environnement que some_filevous avez déjà, et elle s'appellerait. Il serait assez facile de le savoir:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege
2

eval "$(declare -F | sed -e 's/-f /-fx /')"exportera toutes les fonctions.

Je fais cela souvent avant de démarrer un shell interactif dans un script pour pouvoir déboguer et travailler dans le contexte du script tout en utilisant ses fonctions et ses variables.

Exemple:

eval "$(declare -F | sed -e 's/-f /-fx /')"
export SOME IMPORTANT VARIABLES AND PASSWORDS
bash -i
Schlomo
la source
1

Les fonctions ne sont pas exportées vers des sous-processus. C'est pourquoi il existe des fichiers nommés .kshrc ou .bashrc: pour définir des fonctions qui devraient également être disponibles dans les sous-shell.

Si vous exécutez un script, les scripts. * Shrc ne sont normalement pas fournis. Vous auriez à coder cela explicitement, comme dans . ~/.kshrc.

ktf
la source
Ainsi, la ~ racine / .bashrc pourrait être une option dans mon cas, car les scripts sont exécutés en tant que root. Merci pour cet indice.
Nils
si vous utilisez les fichiers. * shrc, assurez-vous qu’ils ne forcent pas le comportement interactif (comme l’alias stupide rm=rm -i)
ktf
0

Eh bien, je suis nouveau sur Linux, mais vous pouvez essayer ceci. Dans certains fichiers, appelons-le, 'tmp / general' vous construisez votre fonction:

func1(){
   echo "func from general"
}

Dans votre script, ajoutez:

. /tmp/general

et courir:

func1

Vous aurez à l'écran: func from general.

personne
la source
0
declare -x -f NAME

Plus d'informations

-f restreint l'action ou l'affichage aux noms de fonction et aux définitions
-x pour exporter les NOMS
Steven Penny
la source