通过 ssh 运行嵌套的本地 bash 函数

通过 ssh 运行嵌套的本地 bash 函数

我有一个像这样的脚本,它声明了许多使用其他函数的函数,等等:

#!/bin/bash

function a {
    ...
}

function b {
    ...
    a
    ...
}

...

另一个脚本使用了该脚本中的一些函数:

#!/bin/bash

do_something

source "/path/to/functional/script"

ssh user@host "$(typeset -f f_from_that_script); f_from_that_script"
...

它所说的问题environment: line N: another_f_from_that_script: command not found意味着第一个函数在内部使用第二个函数:

function f_from_that_script {
    ...
    another_f_from_that_script
    ...
}

有没有办法定义所有这些函数以便能够通过 ssh 运行它们?

答案1

不,bash 中的解析器无法进行如此深入的语法分析(并且通常不可能,因为被调用函数的名称可能在运行时计算)。据我所知,没有第三方解析器可以做到这一点,但有论文描述了为什么理论上在所有情况下都不可能做到这一点。我写了一个回答关于那个。

我建议您运行包含所有函数的完整脚本,而不是转发单个函数。您可以通过 scp 将脚本复制到临时位置,然后source通过 ssh 复制它,或者通过 ssh 将整个脚本通过管道传输到 stdin 来实现这一点。

如果您必须定期通过 ssh 执行复杂的功能,那么可能需要使用更强大、更正式的远程管理工具(例如)ansible

答案2

你总能通过全部远程 shell 的已知函数定义:

#! /bin/bash -

do_something

source "/path/to/functional/script" || exit

{
  typeset -f # output all function definitions
  echo f_from_that_script
} | ssh user@host bash

请注意,sshdonhost将运行user的登录 shell 来解释您传递的命令,这不一定是 bash (bash在当今时代,谁会使用它作为登录 shell?)。

另请注意,bash的语法取决于:

  • 区域设置(特别是LC_CTYPE类别:字符集和字符分类)
  • extglob启用的选项集(如)。
  • bash 的版本

因此,如果本地和远程主机之间的 bash 区域设置和版本不同,或者您更改了默认选项,请注意潜在的问题。

答案3

您可以通过以下方式手动加载函数:

. /path/to/functional/script

然后它将将该脚本加载到当前环境中,然后第二个脚本可以访问这些功能。

要使其自动化,只需在 ~/.bashrc 底部添加即可

. /path/to/functional/script

这应该可以解决你的问题。

我一定是误解了这个问题,或者错误出在 ssh 命令中。

这是我的测试:

文件:function_source.sh

#!/bin/bash

function testcall {

echo "now here I am"

}

文件 do_something.sh

#!/bin/bash

source ./function_source.sh

echo -e "This is called from within the script:\r\n"
testcall
echo -e "End Script - Any further output is from the function call:\r\n\r\n"

脚本文件 do_something.sh 加载 function_source.sh 的源文件,然后调用 function_source.sh 中的函数 testcall。

ssh %server% "./do_something.sh; testcall"

输出:

-ssh %server% "./do_something.sh; testcall"

这是从脚本内部调用的:

现在我在这里

结束脚本 - 任何进一步的输出都来自函数调用:

bash:第 1 行:testcall:未找到命令

现在,如果我引用 function_source.sh is .bashrc 并将以下行添加到 .bashrc 的末尾:

. ./function_source.sh

现在我运行相同的命令:

ssh %server% "./do_something.sh; testcall"

输出是:

-ssh %server% "./do_something.sh; testcall"

这是从脚本内部调用的:

现在我在这里

结束脚本 - 任何进一步的输出都来自函数调用:

现在我在这里

引用 .bashrc 中的文件不起作用的唯一方法是,如果 .bashrc 中有以下内容:

case $- in
    *i*) ;;
      *) return;;
esac

如果您的 .bashrc 中确实有该代码,那么当您使用 ssh 运行命令时,它不会加载您的 .bashrc 环境,因此也不会加载您的源文件。

注意:这就是为什么您不只使用标准 bashrc 和脚本文件,您可以自己从头开始编写它们,或者阅读每一行并知道每一行的作用。

为了覆盖其他设置,首先加载源文件,然后调用该函数 - 注意:我已经删除了 bashrc 中对源代码的引用:

命令:

ssh %server% ". ~/function_source.sh; testcall"

输出:

-ssh %server% ".~/function_source.sh; testcall"

现在我在这里

最后,在 bashrc 中加载源文件,然后使用 ssh 调用该函数。

命令:

ssh %server% "testcall"

输出:

-ssh%server%“testcall”

现在我在这里

如果您需要更多示例,请告诉我。

注意:我使用 Windows 命令提示符(修改后的提示符为 -)来运行 ssh 命令,出于隐私原因使用 %server% 变量,并使用 ssh 密钥进行身份验证。

为了确保我掩盖了所有事情,我转移到了我的一个 Linux 虚拟机中的 ssh,这样我就可以按照 terdon 的建议通过 ssh 到本地主机。

我还修改了函数源:

-cat function_source.sh
#!/bin/bash

function testcall {

echo -e "now here I am\r\n\r\n"
testanother
}

function testanother {
        echo -e "this is another function\r\n\r\n"
}
 

如果我运行命令:

ssh localhost "$(typeset -f testcall); testcall"

输出:

-ssh localhost“$(排版-f testcall); testcall”

现在我在这里

但是,如果我运行命令:

ssh localhost "$(cat function_source.sh); testcall"

输出:

-ssh localhost“$(cat function_source.sh); testcall”

现在我在这里

这是另一个函数

仅当您没有 ssh 连接到的服务器的写入权限并且需要每次将函数加载到环境中时,您才需要执行此操作。

相关内容