Kann ich Funktionen in bash "exportieren"?


81
source some_file

some_file:

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

Wenn ich some_file schreibe, sind die Funktion "doit" und die Variable TEST in der Kommandozeile verfügbar. Aber dieses Skript ausführen:

script.sh:

#/bin/sh
echo $TEST
doit test2

Gibt den Wert von TEST zurück, generiert jedoch einen Fehler bezüglich der unbekannten Funktion "doit".

Kann ich die Funktion auch "exportieren" oder muss ich some_file in script.sh angeben, um die Funktion dort zu verwenden?


2
zusammenfasst Antworten unten (enzotib korrekt ist, Sie bash verwenden können , vorausgesetzt, da die Frage angibt): Wechsel #!/bin/shzu #!/bin/bashund nach doit() {...} nurexport -f doit
michael

Nur zur Veranschaulichung: Diese Lösung funktioniert normalerweise auch, wenn Sie sie verwenden. Es #!/bin/shempfiehlt sich jedoch, sie zu verwenden #!/bin/bash, um Probleme zu vermeiden, wenn die Standard-Shell nicht bash ist.
Nagel

Antworten:


120

In Bash können Sie Funktionsdefinitionen mit in die Sub-Shell exportieren

export -f function_name

Zum Beispiel können Sie dieses einfache Beispiel ausprobieren:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Wenn Sie dann anrufen ./script1, sehen Sie die Ausgabe Hallo! .


16

Durch "Exportieren" einer Funktion mit export -fwird eine Umgebungsvariable mit dem Funktionskörper erstellt. Betrachten Sie dieses Beispiel:

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

Dies bedeutet, dass nur die Shell (nur Bash?) Die Funktion akzeptieren kann. Sie können die Funktion auch selbst einstellen, da der Bash nur Envvars berücksichtigt, die mit () {als Funktion beginnen:

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

Wenn Sie diese Variable über SSH "exportieren" müssen, benötigen Sie die Funktion wirklich als Zeichenfolge. Dies kann mit der print-Option ( -p) für Funktionen ( -f) des declareeingebauten getan werden :

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

Dies ist sehr nützlich, wenn Sie komplexeren Code haben, der über SSH ausgeführt werden muss. Betrachten Sie das folgende fiktive Skript:

#!/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 fn2hat bei mir nicht funktioniert. Auf Arch Linux mit shBash v5.0.7 habe ich bekommen sh: fn2: command not found. Auf Ubuntu mit mit shDash v0.2.3 habe ich bekommen sh: 1: fn2: not found. Welche shShell hast du benutzt?
Socowi

Ich denke deine Antwort ist falsch. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'druckt nichts für mich, wird also eindeutig fnicht als reine Zeichenkette exportiert. Ich habe mit beiden oben genannten Kombinationen getestet.
Socowi

Und selbst wenn die Funktion als Zeichenfolge exportiert wurde, warum sollte shdiese Zeichenfolge als Funktion ausgeführt werden? In Ihrem Beispiel ist fn2='() { echo Hi;}' sh -c fn2der Befehl, der an fn2gegeben sh wird, buchstäblich die Zeichenfolge "fn2". shnach einem solchen Befehl in seinem PATH suchen, aber nicht nach einer Variablen suchen $fn2, diese erweitern und ihren Wert als Funktion ausführen - das klingt für mich nach einem großen Sicherheitsproblem. edit: Ich denke das wars! War Ihr gezeigtes Verhalten der Sicherheitsfehler, der als ShellShock / Bashdoor bekannt ist ?
Socowi

In Bash 5.0.7 (Arch Linux) scheint ein Präfix vorangestellt zu sein. Dies geschah wahrscheinlich als Reaktion auf ShellShock. Beispiel: fn(){ echo foo; }; export -f fn; env | grep fooAusgängeBASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

Aufbauend auf @ Lekensteyns Antwort ...

Bei Verwendung declare -pfwerden alle zuvor in der aktuellen Shell definierten Funktionen an STDOUT ausgegeben.

An diesem Punkt können Sie STDOUT an einen beliebigen Ort umleiten und die zuvor definierten Funktionen praktisch an einen beliebigen Ort übertragen.

Die folgende Antwort fügt sie in eine Variable ein. Dann geben wir diese Variable plus den Aufruf der Funktion wieder, die wir in die neue Shell ausführen möchten, die als neuer Benutzer erzeugt wird. Wir tun dies, indem wir sudoden Schalter -u(aka. user) Verwenden und einfach Bash ausführen (was das gepipte STDOUT als die auszuführende Eingabe erhält).

Da wir wissen, dass wir von einer Bash-Shell zu einer Bash-Shell wechseln, wissen wir, dass Bash die zuvor definierten Funktionen der Shells korrekt interpretiert. Die Syntax sollte in Ordnung sein, solange zwischen einer Bash-Shell derselben Version und einer neuen Bash-Shell derselben Version gewechselt wird.

YMMV, wenn Sie zwischen verschiedenen Shells oder zwischen Systemen wechseln, auf denen möglicherweise unterschiedliche Versionen von Bash installiert sind.

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

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

5

Sie können keine Funktionen exportieren, nicht so, wie Sie es beschreiben. Die Shell lädt die ~/.bashrcDatei nur beim Start einer interaktiven Shell (Suche nach "Invocation" in der bash-Manpage ).

Sie können eine "Bibliothek" erstellen, die beim Starten des Programms geladen wird:

source "$HOME/lib/somefile"

Und platzieren Sie Ihre nicht interaktiven Funktionen und Einstellungen dort.


Also muss ich entweder die Subshell mit dem "login" -Parameter starten (um ~ / .profile zu analysieren) oder diese Datei als Quelle verwenden.
Nils

Wenn Sie nicht interaktive Shells etwas genauer betrachten, können Sie die BASH_ENVUmgebungsvariable so einstellen , some_filedass sie bereits vorhanden ist, und sie wird aufgerufen. Es wäre leicht genug, das herauszufinden:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege

2

eval "$(declare -F | sed -e 's/-f /-fx /')"exportiert alle Funktionen.

Ich mache das oft, bevor ich eine interaktive Shell in einem Skript starte, damit ich im Skriptkontext debuggen und arbeiten kann, während ich seine Funktionen und Variablen verwende.

Beispiel:

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

1

Funktionen werden nicht in Unterprozesse exportiert. Aus diesem Grund gibt es Dateien mit den Namen .kshrc oder .bashrc: Zum Definieren von Funktionen, die auch in Subshells verfügbar sein sollen.

Wenn Sie ein Skript ausführen, werden die. * Shrc-Skripte normalerweise nicht bereitgestellt. Sie müssten das explizit codieren, wie in . ~/.kshrc.


Das ~ root / .bashrc könnte in meinem Fall eine Option sein, da die Skripte als root ausgeführt werden. Danke für diesen Hinweis.
Nils

Wenn Sie die. * shrc-Dateien verwenden, stellen Sie sicher, dass sie kein interaktives Verhalten erzwingen (wie den dummen Alias rm=rm -i)
ktf

0

Nun, ich bin neu in Linux, aber Sie können dies versuchen. Nennen wir es in einer Datei "tmp / general". Sie erstellen Ihre Funktion:

func1(){
   echo "func from general"
}

Fügen Sie in Ihrem Shell-Skript Folgendes hinzu:

. /tmp/general

und Renn:

func1

Sie werden auf dem Bildschirm angezeigt: func from general.


0
declare -x -f NAME

Mehr Info

-f Aktion oder Anzeige auf Funktionsnamen und Definitionen beschränken
-x, damit NAMEs exportiert werden
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.