Posso "esportare" le funzioni in bash?


81
source some_file

un_file:

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

Se ho source some_file la funzione "doit" e la variabile TEST sono disponibili sulla riga di comando. Ma eseguendo questo script:

script.sh:

#/bin/sh
echo $TEST
doit test2

Restituirà il valore di TEST, ma genererà un errore relativo alla funzione sconosciuta "doit".

Posso "esportare" anche la funzione o devo sorgente some_file in script.sh per usare lì la funzione?


2
riepilogando le risposte di seguito (enzotib è corretto, supponendo che tu possa usare bash, come indica la domanda): #!/bin/shpassa a #!/bin/bashe dopo doit() {...} appenaexport -f doit
michael

Solo per la cronaca: questa soluzione di solito funziona anche quando si usa #!/bin/sh, ma è buona norma usarla in #!/bin/bashmodo da evitare problemi quando la shell predefinita non è bash.
Nagel,

Risposte:


120

In Bash puoi esportare le definizioni delle funzioni nella sotto-shell con

export -f function_name

Ad esempio puoi provare questo semplice esempio:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Quindi se chiami ./script1vedrai l'output Hello! .


16

"Esportare" una funzione usando export -fcrea una variabile d'ambiente con il corpo della funzione. Considera questo esempio:

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

Ciò significa che solo la shell (solo Bash?) Sarà in grado di accettare la funzione. Puoi anche impostare tu stesso la funzione poiché Bash considera solo gli avvocati che iniziano () {come funzione:

$ 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'

Se devi "esportare" questa variabile su SSH, allora hai davvero bisogno della funzione come stringa. Questo può essere fatto con l'opzione di stampa ( -p) per le funzioni ( -f) del declarebuilt-in:

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

Questo è molto utile se si dispone di codice più complesso che deve essere eseguito su SSH. Considera la seguente sceneggiatura fittizia:

#!/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"

fn2='() { echo Hi;}' sh -c fn2Non ha funzionato per me. Su arch linux con shessere bash v5.0.7 ho ottenuto sh: fn2: command not found. Su Ubuntu con l' shessere dash v0.2.3 ho ottenuto sh: 1: fn2: not found. Quale shshell hai usato?
Socowi,

Penso che la tua risposta sia sbagliata. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'non stampa nulla per me, quindi chiaramente fnon viene esportato come una semplice stringa. Ho provato con entrambe le combinazioni di cui sopra.
Socowi,

E anche se la funzione fosse esportata come stringa, perché sheseguire quella stringa come funzione? Nel tuo esempio fn2='() { echo Hi;}' sh -c fn2il comando fn2dato sh è letteralmente la stringa "fn2". shdovrebbe cercare un tale comando nel suo PERCORSO ma non dovrebbe cercare se c'è una variabile $fn2, espandere quella variabile ed eseguire il suo valore come funzione - mi sembra un grosso problema di sicurezza. modifica: penso che sia tutto! Il tuo comportamento mostrato era un bug di sicurezza noto come ShellShock / Bashdoor ?
Socowi,

Con Bash 5.0.7 (Arch Linux), sembra che sia stato anteposto un prefisso. Ciò è stato probabilmente fatto in risposta a ShellShock. Esempio: fn(){ echo foo; }; export -f fn; env | grep foousciteBASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

Sulla base della risposta di @ Lekensteyn ...

Se lo si utilizza declare -pf, STDOUT produrrà tutte le funzioni precedentemente definite nella shell corrente.

A quel punto puoi reindirizzare STDOUT dove vuoi e in effetti riempire le funzioni precedentemente definite dove vuoi.

La seguente risposta li inserirà in una variabile. Quindi riecheggiamo quella variabile più l'invocazione della funzione che vogliamo eseguire nella nuova shell che viene generata come nuovo utente. Facciamo questo usando sudolo switch -u(aka. user) E semplicemente eseguendo Bash (che riceverà lo STDOUT convogliato come input da eseguire).

Poiché sappiamo che stiamo passando da una shell Bash a una shell Bash, sappiamo che Bash interpreterà correttamente le funzioni definite dalle precedenti shell. La sintassi dovrebbe andare bene finché ci spostiamo da una shell Bash della stessa versione a una nuova shell Bash della stessa versione.

YMMV se ci si sposta tra shell diverse o tra sistemi che possono avere versioni diverse di Bash.

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

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

5

Non è possibile esportare funzioni, non nel modo in cui si sta descrivendo. La shell caricherà il ~/.bashrcfile solo all'inizio di una shell interattiva (cerca "Invocazione" nella manpage di bash ).

Quello che puoi fare è creare una "libreria" che viene caricata all'avvio del programma:

source "$HOME/lib/somefile"

E posiziona lì le tue funzioni e impostazioni non interattive.


Quindi devo avviare la subshell con il parametro "login" (per analizzare ~ / .profile) o sorgente quel file.
Nils,

Osservando un po 'più da vicino, su shell non interattive, potresti impostare BASH_ENVla variabile d'ambiente che some_filehai già, e sarebbe chiamata. Sarebbe abbastanza facile scoprirlo:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege,

2

eval "$(declare -F | sed -e 's/-f /-fx /')"esporterà tutte le funzioni.

Lo faccio molto prima di avviare una shell interattiva in uno script per consentirmi di eseguire il debug e lavorare nel contesto dello script mentre ne utilizzo le funzioni e le variabili.

Esempio:

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

1

Le funzioni non vengono esportate in sottoprocessi. Questo è il motivo per cui ci sono file denominati .kshrc o .bashrc: per definire le funzioni che dovrebbero essere disponibili anche nei subshells.

Se si esegue uno script, gli script .c shrc normalmente non vengono forniti. Dovresti codificarlo esplicitamente, come in . ~/.kshrc.


Quindi ~ root / .bashrc potrebbe essere un'opzione nel mio caso, poiché gli script vengono eseguiti come root. Grazie per quel suggerimento.
Nils,

se usi i file. * shrc, assicurati che non forzino il comportamento interattivo (come lo stupido alias rm=rm -i)
ktf

0

Bene, sono nuovo di Linux, ma puoi provare questo. In alcuni file, chiamiamolo 'tmp / general', costruisci la tua funzione:

func1(){
   echo "func from general"
}

Nel tuo script di shell aggiungi:

. /tmp/general

e corri:

func1

Otterrete sullo schermo: func from general.


0
declare -x -f NAME

Ulteriori informazioni

-f limita l'azione o la visualizzazione ai nomi e alle definizioni delle funzioni
-x per esportare NAME
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.