我有一个像这样的脚本,它声明了许多使用其他函数的函数,等等:
#!/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
请注意,sshd
onhost
将运行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 连接到的服务器的写入权限并且需要每次将函数加载到环境中时,您才需要执行此操作。