Чи можу я "експортувати" функції в bash?


80
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щоб уникнути проблем, коли оболонка за замовчуванням не баш.
Nagel

Відповіді:


119

У 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, функція вам дійсно потрібна як рядок. Це можна зробити за допомогою опції друку ( -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не працював для мене. На арку linux з shбасом v5.0.7 я потрапив sh: fn2: command not found. На Ubuntu з тим, shщо тире 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 :
Арседж

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

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

-обмежити дію чи показ лише іменами та визначеннями функцій
-x, щоб зробити імпорт NAMEs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.