¿Puedo "exportar" funciones en bash?


81
source some_file

algún_archivo:

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

Si obtengo algún_archivo, la función "doit" y la variable TEST están disponibles en la línea de comando. Pero ejecutando este script:

script.sh:

#/bin/sh
echo $TEST
doit test2

Devolverá el valor de TEST, pero generará un error sobre la función desconocida "doit".

¿Puedo "exportar" la función también, o tengo que buscar algún_archivo en script.sh para usar la función allí?


2
Resumiendo las respuestas a continuación (enzotib es correcto, suponiendo que puede usar bash, como lo indica la pregunta): cambie #!/bin/sha #!/bin/bashy después doit() {...} soloexport -f doit
michael

Solo para el registro: esta solución generalmente funcionará cuando la use #!/bin/shtambién, pero es una buena práctica usarla #!/bin/bashpara evitar problemas cuando el shell predeterminado no es bash.
Nagel

Respuestas:


120

En Bash puede exportar definiciones de funciones a sub-shell con

export -f function_name

Por ejemplo, puedes probar este sencillo ejemplo:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Entonces, si llama ./script1, verá la salida ¡Hola! .


16

"Exportar" una función usando export -fcrea una variable de entorno con el cuerpo de la función. Considere este ejemplo:

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

Esto significa que solo el shell (¿solo Bash?) Podrá aceptar la función. También puede configurar la función usted mismo ya que Bash solo considera envvars que comienzan () {como una función:

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

Si necesita "exportar" esta variable a través de SSH, entonces realmente necesita la función como una cadena. Esto se puede hacer con la opción de impresión ( -p) para las funciones ( -f) de la función declareintegrada:

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

Esto es muy útil si tiene un código más complejo que debe ejecutarse a través de SSH. Considere el siguiente script ficticio:

#!/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 fn2no funciono para mi En arch linux con shsiendo bash v5.0.7 obtuve sh: fn2: command not found. En Ubuntu con shsiendo dash v0.2.3 obtuve sh: 1: fn2: not found. ¿Qué shcaparazón usaste?
Socowi

Creo que tu respuesta es incorrecta. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'no imprime nada para mí, por lo que claramente fno se exporta como una cadena simple. Probé con las dos combinaciones mencionadas anteriormente.
Socowi

E incluso si la función se exportó como una cadena, ¿por qué debería shejecutar esa cadena como una función? En su ejemplo, fn2='() { echo Hi;}' sh -c fn2el comando fn2dado sh es literalmente la cadena "fn2". shdebería buscar dicho comando en su RUTA, pero no debería buscar si hay una variable $fn2, expandir esa variable y ejecutar su valor como una función; me parece un problema de seguridad importante. editar: creo que eso es todo! ¿Fue su comportamiento mostrado el error de seguridad conocido como ShellShock / Bashdoor ?
Socowi

Con Bash 5.0.7 (Arch Linux), parece que se antepone un prefijo. Esto probablemente se hizo en respuesta a ShellShock. Ejemplo: fn(){ echo foo; }; export -f fn; env | grep foosalidasBASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

Sobre la base de la respuesta de @ Lekensteyn ...

Si lo usa declare -pf, enviará todas las funciones definidas previamente en el shell actual a STDOUT.

En ese momento, puede redirigir STDOUT a donde quiera y, de hecho, rellenar las funciones definidas previamente donde quiera.

La siguiente respuesta los convertirá en una variable. Luego, hacemos eco de esa variable más la invocación de la función que queremos ejecutar en el nuevo shell que se genera como un nuevo usuario. Hacemos esto mediante el uso sudodel -umodificador (también conocido como user) y simplemente ejecutando Bash (que recibirá el STDOUT canalizado como entrada para ejecutar).

Como sabemos que vamos de un shell Bash a un shell Bash, sabemos que Bash interpretará correctamente las funciones definidas de shells anteriores. La sintaxis debe estar bien siempre que nos traslademos entre un shell Bash de la misma versión a un nuevo shell Bash de la misma versión.

YMMV si se está moviendo entre diferentes shells o entre sistemas que pueden tener diferentes versiones de Bash.

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

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

5

No puede exportar funciones, no de la manera que está describiendo. El shell solo cargará el ~/.bashrcarchivo al inicio de un shell interactivo (busque "Invocación" en la página de manual de bash ).

Lo que puede hacer es crear una "biblioteca" que se carga cuando inicia el programa:

source "$HOME/lib/somefile"

Y coloque sus funciones y configuraciones no interactivas allí.


Así que tengo que iniciar el subshell con el parámetro "login" (para analizar ~ / .profile) o buscar ese archivo.
Nils

Mirando un poco más de cerca, en shells no interactivos, podría establecer BASH_ENVuna variable de entorno some_fileque ya tiene, y se llamaría. Sería bastante fácil descubrirlo:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege

2

eval "$(declare -F | sed -e 's/-f /-fx /')"exportará todas las funciones.

Hago esto mucho antes de comenzar un shell interactivo en un script para permitirme depurar y trabajar en el contexto del script mientras uso sus funciones y variables.

Ejemplo:

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

1

Las funciones no se exportan a subprocesos. Es por eso que hay archivos llamados .kshrc o .bashrc: para definir funciones que también deberían estar disponibles en subcapas.

Si ejecuta una secuencia de comandos, las secuencias de comandos. * Shrc normalmente no se obtienen. Tendría que codificar eso explícitamente, como en . ~/.kshrc.


Entonces ~ root / .bashrc podría ser una opción en mi caso, ya que los scripts se ejecutan como root. Gracias por esa pista.
Nils

. si utilizar los archivos * SHRC, asegúrese ellos no forzar el comportamiento interactivo (como los estúpidos alias rm=rm -i)
ktf

0

Bueno, soy nuevo en Linux, pero puedes probar esto. En algún archivo, llamémoslo, 'tmp / general' construyes tu función:

func1(){
   echo "func from general"
}

En su script de shell, agregue:

. /tmp/general

y correr:

func1

Usted obtendrá en la pantalla: func from general.


0
declare -x -f NAME

Más información

-f restringir la acción o visualización a nombres y definiciones de funciones
-x para hacer que las NAME exporten
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.