Posso "exportar" funções no bash?


81
source some_file

some_file:

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

Se eu for fonte de algum arquivo, a função "doit" e a variável TEST estarão disponíveis na linha de comando. Mas executando este script:

script.sh:

#/bin/sh
echo $TEST
doit test2

Retornará o valor de TEST, mas gerará um erro sobre a função desconhecida "doit".

Também posso "exportar" a função ou tenho que usar algum_arquivo no script.sh para usar a função lá?


2
resumindo respostas abaixo (enzotib está correto, supondo que você pode usar bash, como a questão indica): mudança #!/bin/shpara #!/bin/bashe depois doit() {...} apenasexport -f doit
michael

Apenas para constar: Essa solução geralmente também funciona quando você usa #!/bin/sh, mas é uma boa prática usá- #!/bin/bashlo para evitar problemas quando o shell padrão não é o bash.
Nagel

Respostas:


120

No Bash, você pode exportar definições de funções para sub-shell com

export -f function_name

Por exemplo, você pode tentar este exemplo simples:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Então, se você ligar ./script1, verá a saída Olá! .


16

"Exportar" uma função usando export -fcria uma variável de ambiente com o corpo da função. Considere este exemplo:

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

Isso significa que apenas o shell (apenas Bash?) Poderá aceitar a função. Você também pode definir a função, pois o Bash considera apenas os envvars começando com () {como function:

$ 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 você precisar "exportar" essa variável pelo SSH, precisará realmente da função como uma string. Isso pode ser feito com a opção de impressão ( -p) para funções ( -f) do declarebuilt-in:

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

Isso é muito útil se você tiver um código mais complexo que precise ser executado no SSH. Considere o seguinte script fictício:

#!/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 fn2não funcionou para mim. No arch linux com shbash v5.0.7 eu consegui sh: fn2: command not found. No Ubuntu, com shsendo traço v0.2.3 eu consegui sh: 1: fn2: not found. Qual shshell você usou?
Socowi 02/07

Eu acho que sua resposta está errada. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'não imprime nada para mim, portanto, claramente fnão é exportado como uma sequência simples. Eu testei com as duas combinações mencionadas acima.
Socowi 02/07

E mesmo se a função foi exportada como uma sequência, por que deve shexecutar essa sequência como uma função? No seu exemplo, fn2='() { echo Hi;}' sh -c fn2o comando fn2dado sh é literalmente a string "fn2". shdeve procurar esse comando em seu PATH, mas não deve procurar se há uma variável $fn2, expandir essa variável e executar seu valor como uma função - parece-me um grande problema de segurança. edit: Eu acho que é isso! Seu comportamento demonstrado foi o bug de segurança conhecido como ShellShock / Bashdoor ?
Socowi 02/07

Com o Bash 5.0.7 (Arch Linux), parece que um prefixo é anexado. Provavelmente, isso foi feito em resposta ao ShellShock. Exemplo: fn(){ echo foo; }; export -f fn; env | grep foosaídasBASH_FUNC_fn%%=() { echo foo
Lekensteyn 02/07

7

Com base na resposta de @ Lekensteyn ...

Se você usá- declare -pflo, exibirá todas as funções definidas anteriormente no shell atual para STDOUT.

Nesse ponto, você pode redirecionar o STDOUT para onde quiser e, com efeito, colocar as funções definidas anteriormente onde quiser.

A resposta a seguir os colocará em uma variável. Em seguida, repetimos essa variável mais a invocação da função que queremos executar no novo shell que é gerado como um novo usuário. Fazemos isso usando sudoo comutador -u(aka. user) E simplesmente executando o Bash (que receberá o STDOUT canalizado como a entrada a ser executada).

Como sabemos que estamos passando de um shell Bash para um shell Bash, sabemos que o Bash interpretará corretamente as funções definidas pelos shells anteriores. A sintaxe deve estar correta desde que nos movamos entre um shell Bash da mesma versão para um novo shell Bash da mesma versão.

YMMV se você estiver se deslocando entre diferentes shells ou entre sistemas que podem ter versões diferentes do Bash.

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

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

5

Você não pode exportar funções, não da maneira que está descrevendo. O shell carregará o ~/.bashrcarquivo apenas no início de um shell interativo (procure "Invocação" na página de manual do bash ).

O que você pode fazer é criar uma "biblioteca" que é carregada quando você inicia o programa:

source "$HOME/lib/somefile"

E coloque suas funções e configurações não interativas lá.


Então, eu tenho que iniciar o subshell com o parâmetro "login" (para analisar ~ / .profile) ou originar esse arquivo.
Nils

Observando um pouco mais de perto, em shells não interativos, você pode definir BASH_ENVa variável de ambiente para que some_filevocê já tenha, e isso seria chamado. Seria fácil descobrir isso:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege 17/10

2

eval "$(declare -F | sed -e 's/-f /-fx /')"exportará todas as funções.

Faço isso muito antes de iniciar um shell interativo em um script para me permitir depurar e trabalhar no contexto do script enquanto uso suas funções e variáveis.

Exemplo:

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

1

As funções não são exportadas para subprocessos. É por isso que existem arquivos denominados .kshrc ou .bashrc: para definir funções que também devem estar disponíveis em subshells.

Se estiver executando um script, os scripts. * Shrc normalmente não são originados. Você teria que codificar isso explicitamente, como em . ~/.kshrc.


Portanto, o ~ root / .bashrc pode ser uma opção no meu caso, já que os scripts são executados como root. Obrigado por essa dica.
Nils

. se estiver usando os arquivos * SHRC, certifique-se que eles não forçar comportamento interativo (como o estúpido apelido rm=rm -i)
KTF

0

Bem, eu sou novo no Linux, mas você pode tentar isso. Em algum arquivo, vamos chamá-lo de 'tmp / general', você cria sua função:

func1(){
   echo "func from general"
}

No seu script de shell, adicione:

. /tmp/general

e corra:

func1

Você vai ter na tela: func from general.


0
declare -x -f NAME

Mais informações

-f restringe a ação ou exibe os nomes e definições das funções
-x para exportar NAMEs
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.