Czy mogę „eksportować” funkcje w bash?

80
source some_file

jakiś plik:

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

Jeśli podam jakiś plik, funkcja „doit” i zmienna TEST są dostępne w wierszu poleceń. Ale uruchomienie tego skryptu:

script.sh:

#/bin/sh
echo $TEST
doit test2

Zwróci wartość TEST, ale wygeneruje błąd dotyczący nieznanej funkcji „doit”.

Czy mogę również „wyeksportować” tę funkcję, czy też muszę pobrać plik_pliku w script.sh, aby użyć tej funkcji?

Nils
źródło
2
podsumowując odpowiedzi poniżej (enzotib jest poprawna, zakładając, że można użyć bash, jak kwestia wskazuje): zmiany #!/bin/shna #!/bin/bashi po doit() {...} prostuexport -f doit
Michael
Dla przypomnienia: to rozwiązanie zwykle będzie działać, gdy go użyjesz #!/bin/sh, ale dobrą praktyką jest używanie go #!/bin/bash, aby uniknąć problemów, gdy domyślna powłoka nie jest bash.
Nagel
stackoverflow.com/questions/1885871/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Odpowiedzi:

119

W Bash możesz eksportować definicje funkcji do podpowłoki za pomocą

export -f function_name

Na przykład możesz wypróbować ten prosty przykład:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Jeśli zadzwonisz ./script1, zobaczysz wynik Hello! .

enzotib
źródło
16

„Eksportowanie” funkcji za pomocą export -ftworzy zmienną środowiskową z treścią funkcji. Rozważ ten przykład:

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

Oznacza to, że tylko powłoka (tylko Bash?) Będzie mogła zaakceptować tę funkcję. Możesz także ustawić tę funkcję samodzielnie, ponieważ Bash bierze pod uwagę envvary zaczynające się od () {jako:

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

Jeśli potrzebujesz „wyeksportować” tę zmienną przez SSH, to naprawdę potrzebujesz tej funkcji jako łańcucha. Można to zrobić za pomocą opcji drukowania ( -p) dla funkcji ( -f) declarewbudowanego:

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

Jest to bardzo przydatne, jeśli masz bardziej złożony kod, który należy wykonać przez SSH. Rozważ następujący fikcyjny skrypt:

#!/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
źródło
fn2='() { echo Hi;}' sh -c fn2nie działało dla mnie. Na arch Linux z shbash v5.0.7 mam sh: fn2: command not found. Na Ubuntu z shbyciem dash v0.2.3 mam sh: 1: fn2: not found. Z której shpowłoki korzystałeś?
Socowi
Myślę, że twoja odpowiedź jest zła. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'nic dla mnie nie drukuje, więc najwyraźniej fnie jest eksportowany jako zwykły ciąg. Testowałem z obydwoma wyżej wymienionymi kombinacjami.
Socowi
I nawet jeśli funkcja została wyeksportowana jako ciąg, to po co shwykonywać ten ciąg jako funkcję? W twoim przykładzie podane fn2='() { echo Hi;}' sh -c fn2polecenie jest dosłownie ciągiem . powinien szukać takiej komendy w PATH, ale nie powinien sprawdzać, czy istnieje zmienna , rozwijać tę zmienną i wykonywać jej wartość jako funkcję - brzmi to dla mnie poważnie. edycja: Myślę, że to wszystko! Czy pokazane zachowanie było błędem bezpieczeństwa znanym jako ShellShock / Bashdoor ? fn2sh"fn2"sh$fn2
Socowi
W Bash 5.0.7 (Arch Linux) wydaje się, że przedrostek jest poprzedzony. Prawdopodobnie zostało to zrobione w odpowiedzi na ShellShock. Przykład: fn(){ echo foo; }; export -f fn; env | grep foowynikiBASH_FUNC_fn%%=() { echo foo
Lekensteyn
7

Opierając się na odpowiedzi @ Lekensteyn ...

Jeśli declare -pfgo użyjesz , wyświetli wszystkie poprzednio zdefiniowane funkcje w bieżącej powłoce do STDOUT.

W tym momencie możesz przekierować STDOUT tam, gdzie chcesz i w efekcie wstawić wcześniej zdefiniowane funkcje w dowolne miejsce.

Poniższa odpowiedź umieści je w zmiennej. Następnie echo tej zmiennej plus wywołanie funkcji, którą chcemy uruchomić w nowej powłoce, która jest spawnowana jako nowy użytkownik. Robimy to za pomocą sudoprzełącznika -u(aka. user) I po prostu uruchamiając Bash (który otrzyma potokowy STDOUT jako dane wejściowe do uruchomienia).

Ponieważ wiemy, że przechodzimy z powłoki Bash do powłoki Bash, wiemy, że Bash poprawnie interpretuje funkcje zdefiniowane przez poprzednie powłoki. Składnia powinna być poprawna, o ile przechodzimy między jedną powłoką Bash tej samej wersji do nowej powłoki Bash tej samej wersji.

YMMV, jeśli poruszasz się między różnymi powłokami lub między systemami, które mogą mieć różne wersje Bash.

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

FUNCTIONS=`declare -pf`; echo "$FUNCTIONS ; foo" | sudo -u otheruser bash
# $./test.sh
# hello from otheruser
Jesse
źródło
5

Nie można eksportować funkcji, nie w sposób, który opisuje. Powłoka załaduje ~/.bashrcplik tylko na początku interaktywnej powłoki (wyszukaj „Invocation” na stronie podręcznika bash ).

Co możesz zrobić, to utworzyć „bibliotekę”, która jest ładowana podczas uruchamiania programu:

source "$HOME/lib/somefile"

I umieść tam swoje nieinteraktywne funkcje i ustawienia.

Arcege
źródło
Muszę więc albo uruchomić podpowłokę z parametrem „login” (aby przeanalizować ~ / .profile) lub pobrać ten plik.
Nils
Przyglądając się trochę bliżej, w przypadku nieinteraktywnych powłok, możesz ustawić BASH_ENVzmienną środowiskową, some_filektórą już masz, i zostanie wywołana. Łatwo byłoby się tego dowiedzieć:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege
2

eval "$(declare -F | sed -e 's/-f /-fx /')"wyeksportuje wszystkie funkcje.

Robię to dużo przed uruchomieniem interaktywnej powłoki w skrypcie, aby umożliwić mi debugowanie i pracę w kontekście skryptu przy użyciu jego funkcji i zmiennych.

Przykład:

eval "$(declare -F | sed -e 's/-f /-fx /')"
export SOME IMPORTANT VARIABLES AND PASSWORDS
bash -i
Schlomo
źródło
1

Funkcje nie są eksportowane do podprocesów. Dlatego istnieją pliki o nazwach .kshrc lub .bashrc: Aby zdefiniować funkcje, które powinny być dostępne również w podpowłokach.

W przypadku uruchamiania skryptu skrypty. * Shrc zwykle nie są pozyskiwane. Będziesz musiał to jawnie zakodować, tak jak w . ~/.kshrc.

ktf
źródło
Więc ~ root / .bashrc może być opcją w moim przypadku, ponieważ skrypty są uruchamiane jako root. Dzięki za podpowiedź.
Nils,
jeśli używasz plików .c * shrc, upewnij się, że nie wymuszają one interaktywnego zachowania (jak głupi alias rm=rm -i)
ktf
0

Cóż, jestem nowy w Linuksie, ale możesz spróbować. W jakimś pliku nazwijmy go „tmp / general”, budujesz swoją funkcję:

func1(){
   echo "func from general"
}

W skrypcie powłoki dodaj:

. /tmp/general

i biegnij:

func1

Dostaniesz na ekranie: func from general.

nikt
źródło
0
declare -x -f NAME

Więcej informacji

-f ograniczyć akcję lub wyświetlanie do nazw funkcji i definicji
-x, aby eksportować NAME
Steven Penny
źródło