Могу ли я «экспортировать» функции в bash?


81
source some_file

some_file:

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

Если я использую исходный код some_file, функция «doit» и переменная TEST доступны в командной строке. Но запускаем этот скрипт:

script.sh:

#/bin/sh
echo $TEST
doit test2

Вернет значение TEST, но сгенерирует ошибку о неизвестной функции "doit".

Могу ли я тоже "экспортировать" функцию, или мне нужно найти файл some_file в script.sh, чтобы использовать эту функцию там?


2
суммируя ответы ниже (enzotib является правильным, если вы можете использовать Баш, как вопрос указывает): изменение #!/bin/shк #!/bin/bashи после doit() {...} того, как толькоexport -f doit
Майклу

Просто для справки: это решение обычно будет работать, когда вы #!/bin/shтоже используете , но это хорошая практика, чтобы #!/bin/bashизбежать проблем, когда оболочкой по умолчанию не является bash.
Нагель

Ответы:


120

В Bash вы можете экспортировать определения функций в суб-оболочку с помощью

export -f function_name

Например, вы можете попробовать этот простой пример:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Тогда, если вы позвоните, ./script1вы увидите вывод Hello! ,


16

«Экспорт» функции с помощью export -fсоздает переменную окружения с телом функции. Рассмотрим этот пример:

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

Это означает, что только оболочка (только Bash?) Сможет принять функцию. Вы также можете установить функцию самостоятельно, поскольку Bash рассматривает только envvars, начинающийся с () {функции:

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

Если вам нужно «экспортировать» эту переменную через SSH, то вам действительно нужна функция в виде строки. Это можно сделать с помощью опции print ( -p) для функций ( -f) declareвстроенного:

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

Это очень полезно, если у вас есть более сложный код, который необходимо выполнить через SSH. Рассмотрим следующий вымышленный сценарий:

#!/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 fn2не работал для меня. На shArch Linux с Bash v5.0.7 я получил sh: fn2: command not found. На Ubuntu с shDash v0.2.3 я получил sh: 1: fn2: not found. Какую shоболочку вы использовали?
Socowi

Я думаю, что ваш ответ неверен. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'ничего не печатает для меня, так что явно fне экспортируется в виде простой строки. Я протестировал обе комбинации, упомянутые выше.
Socowi

И даже если функция была экспортирована как строка, зачем shвыполнять эту строку как функцию? В вашем примере fn2='() { echo Hi;}' sh -c fn2команда, fn2заданная sh буквально, является строкой "fn2". shдолжен искать такую ​​команду в своем PATH, но не должен искать, есть ли переменная $fn2, раскрывать эту переменную и выполнять ее значение как функцию - для меня это звучит как серьезная проблема безопасности. редактировать: я думаю, что это все! Было ли ваше показанное поведение ошибкой в ​​безопасности, известной как ShellShock / Bashdoor ?
Socowi

В Bash 5.0.7 (Arch Linux) кажется, что префикс добавлен. Скорее всего, это было сделано в ответ на ShellShock. Пример: fn(){ echo foo; }; export -f fn; env | grep fooвыходыBASH_FUNC_fn%%=() { echo foo
Лекенштейн

7

Опираясь на ответ @ Лекенштейна ...

Если вы используете declare -pfего, выведите все ранее определенные функции в текущей оболочке в STDOUT.

В этот момент вы можете перенаправить STDOUT туда, куда вам нужно, и, по сути, заполнить ранее определенные функции, куда вы хотите.

Следующий ответ поместит их в переменную. Затем мы повторяем эту переменную плюс вызов функции, которую мы хотим запустить в новую оболочку, которая создается как новый пользователь. Мы делаем это, используя sudoс -u(ака. user) Переключатель и просто запустив Bash (который получит водопроводную STDOUT в качестве входных данных для запуска).

Поскольку мы знаем, что мы идем от оболочки Bash к оболочке Bash, мы знаем, что Bash будет правильно интерпретировать функции, определенные предыдущими оболочками. Синтаксис должен быть в порядке, пока мы перемещаемся между одной оболочкой Bash той же версии и новой оболочкой Bash той же версии.

YMMV, если вы перемещаетесь между разными оболочками или между системами, которые могут иметь разные версии Bash.

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

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

5

Вы не можете экспортировать функции, не так, как вы описываете. Оболочка будет загружать ~/.bashrcфайл только при запуске интерактивной оболочки (поиск «Invocation» на странице руководства bash ).

Что вы можете сделать, это создать «библиотеку», которая загружается при запуске программы:

source "$HOME/lib/somefile"

И разместите там свои неинтерактивные функции и настройки.


Поэтому мне нужно либо запустить подоболочку с параметром "login" (для анализа ~ / .profile), либо создать исходный файл.
Нильс

Если присмотреться поближе, на неинтерактивных оболочках вы можете установить BASH_ENVпеременную окружения, которая у some_fileвас уже есть, и она будет вызвана. Было бы достаточно легко это выяснить:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege

2

eval "$(declare -F | sed -e 's/-f /-fx /')"будет экспортировать все функции.

Я делаю это много до запуска интерактивной оболочки в скрипте, чтобы позволить мне отлаживать и работать в контексте скрипта, используя его функции и переменные.

Пример:

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

1

Функции не экспортируются в подпроцессы. Вот почему существуют файлы с именами .kshrc или .bashrc: для определения функций, которые также будут доступны в подоболочках.

При запуске сценария сценарии. * Shrc обычно не создаются. Вы должны были бы кодировать это явно, как в . ~/.kshrc.


Так что ~ root / .bashrc может быть вариантом в моем случае, так как скрипты запускаются от имени root. Спасибо за эту подсказку.
Нильс

при использовании файлов. * shrc убедитесь, что они не вызывают интерактивное поведение (например, тупой псевдоним rm=rm -i)
ktf

0

Ну, я новичок в Linux, но вы можете попробовать это. В каком-то файле, назовем его «tmp / general», вы создаете свою функцию:

func1(){
   echo "func from general"
}

В вашем сценарии оболочки добавьте:

. /tmp/general

и запустить:

func1

Вы получите на экране: func from general.


0
declare -x -f NAME

Больше информации

-f ограничить действие или отображение именами функций и определениями
-x сделать экспорт NAMEs
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.