我可以在 bash 中“导出”函数吗?

Nil*_*ils 129 bash function

source some_file
Run Code Online (Sandbox Code Playgroud)

some_file:

doit ()
{
  echo doit $1
}
export TEST=true
Run Code Online (Sandbox Code Playgroud)

如果我获取 some_file函数“ doit ”和变量 TEST 在命令行上可用。但是运行这个脚本:

脚本.sh:

#/bin/sh
echo $TEST
doit test2
Run Code Online (Sandbox Code Playgroud)

将返回 TEST 的值,但会产生关于未知函数“doit”的错误。

我也可以“导出”该函数,还是必须在 script.sh 中获取 some_file 才能在那里使用该函数?

enz*_*tib 179

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

export -f function_name
Run Code Online (Sandbox Code Playgroud)

例如你可以试试这个简单的例子:

./script1

#!/bin/bash

myfun() {
    echo "Hello!"
}

export -f myfun
./script2
Run Code Online (Sandbox Code Playgroud)

./script2

#!/bin/bash

myfun
Run Code Online (Sandbox Code Playgroud)

然后,如果您调用,./script1您将看到输出Hello!.

  • 澄清一下,对于那些想知道的人:它_不能_与 zsh 一起使用(根据我的测试)。它也只能在 bash -> bash 中工作,而不能在 bash -> zsh 中工作。 (3认同)

Lek*_*eyn 26

“导出”一个函数 usingexport -f会创建一个带有函数体的环境变量。考虑这个例子:

$ fn(){ echo \'\"\ \ \$; }
$ export -f fn
$ sh -c printenv\ fn
() {  echo \'\"\ \ \$
}
Run Code Online (Sandbox Code Playgroud)

这意味着只有 shell(只是 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'
Run Code Online (Sandbox Code Playgroud)

如果您需要通过 SSH“导出”此变量,那么您确实需要将该函数作为字符串。这可以通过内置-p函数 ( -f)的打印选项 ( )来完成declare

$ declare -pf fn
fn () 
{ 
    echo \'\"\ \ \$
}
Run Code Online (Sandbox Code Playgroud)

如果您有更复杂的代码需要通过 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"
Run Code Online (Sandbox Code Playgroud)

  • 在 Bash 5.0.7 (Arch Linux) 中,似乎添加了前缀。这很可能是为了响应 ShellShock。示例:`fn(){ echo foo; }; 出口 -f fn; 环境 | grep foo` 输出 `BASH_FUNC_fn%%=() { echo foo` (3认同)
  • `fn2='() { echo Hi;}' sh -c fn2` 对我不起作用。在 `sh` 是 bash v5.0.7 的 arch linux 上,我得到了 `sh: fn2: command not found`。在 Ubuntu 上,`sh` 是 dash v0.2.3,我得到了 `sh: 1: fn2: not found`。你用的是哪个 `sh` shell? (2认同)
  • 我认为你的答案是错误的。`f(){ 回显 a;}; 导出-ff;回显“$f”;sh -c 'printenv f; echo "$f"'` 不会为我打印任何内容,因此显然 `f` 不会导出为纯字符串。我用上面提到的两种组合进行了测试。 (2认同)
  • 即使函数被导出为一个字符串,为什么 `sh` 要把那个字符串作为一个函数来执行呢?在你的例子中`fn2='() { echo Hi;}' sh -c fn2` 给`sh` 的命令`fn2` 字面上是字符串`"fn2"`。`sh` 应该在其 PATH 中搜索这样的命令,但不应该*查看是否存在变量 `$fn2`,展开该变量,并将其值作为函数执行 - 对我来说这听起来像是一个主要的安全问题。**编辑:**我认为就是这样!您显示的行为是否是称为 [ShellShock/Bashdoor](https://en.wikipedia.org/wiki/Shellshock_(software_bug)) 的安全漏洞? (2认同)

小智 10

@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
Run Code Online (Sandbox Code Playgroud)


Arc*_*ege 5

您不能以您描述的方式导出函数。shell 只会~/.bashrc在交互式 shell 开始时加载文件(在bash 手册页中搜索“调用” )。

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

source "$HOME/lib/somefile"
Run Code Online (Sandbox Code Playgroud)

并将您的非交互式功能和设置放在那里。