我可以在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”的错误。

我也可以“导出”该功能,还是必须在script.sh中提供some_file才能在其中使用该功能?


2
变化:总结如下答案(enzotib是正确的,假设你可以使用bash,因为这个问题表示)#!/bin/sh#!/bin/bash doit() {...}export -f doit
迈克尔

仅作记录:此解决方案通常也可以在您使用时使用#!/bin/sh,但是使用#!/bin/bash此做法是一种好习惯,这样可以避免在默认shell不是bash时出现问题。
内格尔2014年

Answers:


119

在Bash中,您可以使用以下命令将函数定义导出到子Shell:

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仅考虑以() {as 开头的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没有为我工作。在shbash v5.0.7的Arch Linux上,我得到了sh: fn2: command not found。在带有sh破折号v0.2.3的Ubuntu上,我得到了sh: 1: fn2: not foundsh您使用了哪个壳?
索科威

我认为您的回答是错误的。f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'不为我打印任何内容,因此显然f不会导出为纯字符串。我测试了上述两种组合。
索科威

即使将函数导出为字符串,为什么还要sh将该字符串作为函数执行?在您的示例中fn2='() { echo Hi;}' sh -c fn2fn2给出的命令sh 实际上是字符串"fn2"sh应该寻找其路径这样的命令,但应该不会,如果有一个变量看$fn2,展开变量,并执行其值作为函数-听起来像一个重大的安全问题给我。编辑:我认为就是这样!您显示的行为是否就是称为ShellShock / Bashdoor的安全漏洞?
索科威

在Bash 5.0.7(Arch Linux)中,前缀似乎是前缀。这可能是为了响应ShellShock。示例:fn(){ echo foo; }; export -f fn; env | grep foo输出BASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

@Lekensteyn的答案为基础 ...

如果使用declare -pf它,它将在当前shell中将所有先前定义的函数输出到STDOUT。

此时,您可以将STDOUT重定向到所需的任何位置,并且实际上可以将先前定义的功能填充到所需的任何位置。

以下答案会将它们填充到变量中。然后,我们回显该变量以及要运行到作为新用户派生的新shell中的函数的调用。我们通过这样做sudo-u(亦称user)开关,只需运行bash(这将接收管道STDOUT作为输入运行)。

正如我们知道的那样,我们正在从Bash shell过渡到Bash shell,我们知道Bash将正确解释以前的Shell定义的函数。只要我们在一个相同版本的Bash shell到相同版本的新Bash shell之间移动,语法就可以了。

YMMV(如果在不同的Shell之间或在具有不同版本的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在交互式外壳程序的开始处加载文件(在bash联机帮助页中搜索“ Invocation” )。

您可以做的是创建“库”,在启动程序时将其加载:

source "$HOME/lib/somefile"

并将非交互功能和设置放在此处。


因此,我必须使用“ login”参数启动子shell(以解析〜/ .profile)或获取该文件。
尼尔斯,

仔细观察一下,在非交互式shell上,可以将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的文件的原因:定义也应在子Shell中可用的函数。

如果运行脚本,则通常不提供。* shrc脚本。您必须像那样显式地编写代码. ~/.kshrc


因此,在我的情况下,〜root / .bashrc是一个选项,因为脚本是作为root运行的。感谢您的提示。
尼尔斯

如果使用。* shrc文件,请确保它们不强制进行交互行为(如愚蠢的别名rm=rm -i
ktf

0

好吧,我是Linux的新手,但是您可以尝试一下。在某个文件中,我们将其称为“ tmp / general”,以构建函数:

func1(){
   echo "func from general"
}

在您的shell脚本中添加:

. /tmp/general

并运行:

func1

屏幕上将显示:func from general


0
declare -x -f NAME

更多信息

-f将动作或显示限制为函数名称和定义
-x使名称导出
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.