在远程服务器上执行本地函数代码

在远程服务器上执行本地函数代码

我正在更新大约 20 个 bash 脚本,这些脚本执行各种服务器端操作任务、检查状态/发送报告等。在某些环境中,这些脚本将在本地运行,在其他环境中,它们需要远程运行。脚本应该检测它们是否需要远程运行或本地运行,并“做正确的事情”

#!/bin/bash

# Generate the foo report

execute_host=appdev7

if [[ "$execute_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    ssh "$execute_host" < $0
else
    # run report
    echo "Report"
fi

一些脚本使用共享函数和环境变量。我可以使用 ssh SendEnv 选项传递环境变量,但我找不到一种在远程运行时使共享函数可用的好方法。

复制代码

# Shared functions and variables

export report_host="appdev7"

function run_report() {
    # run report
    echo 'Report'
}

。/例子

#!/bin/bash

# Generate the foo report

[[ -f shared ]] && source ./shared.sh

export execute_host="$report_host"

if [[ "$execute_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    ssh -o SendEnv='report_host' "$execute_host" < $0
else
    # This doesn't work when the script is run remotely
    run_report
fi
  • 我可以将其放置shared.sh在我们所有的主机上,但我必须以某种方式保持文件同步,而这在某些时候不可避免地会出现问题。
  • 我可以将其放置shared.sh在 NFS 共享上,但如果 NFS 发生故障,我们将无法使用脚本
  • 我可以shared.sh在运行脚本之前 scp 到服务器。这可行,但可能有点慢,而且如果shared.sh依赖于其他脚本,我们还必须复制该文件
  • 我可以使用declare -f来提取函数代码,但我不知道如何将它们转移到远程服务器。函数依赖性也可能导致问题。

我能找到的最干净的解决方案是使用内联运行共享库流程替代

#!/bin/bash

# Generate the foo report

# source the shared code/vars if we're running locally
[[ -f shared.sh ]] && source shared.sh

execute_host="$report_host"

if [[ "$execute_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    # Note that we source the shared library in-line with the script
    ssh -T "$execute_host" < <(cat shared.sh $0)
else
    run_report
fi

我的问题是:

  1. 这种方法有什么问题吗?
  2. 您能找到避免在多个地方引用共享库的方法吗?
  3. 有什么方法可以解决shared.sh? 中的依赖关系(例如,如果shared.sh依赖于shared2.sh
  4. 有没有更好的方法来解决此问题?

答案1

因此,我使用了一种不同的方法来完成这项任务。首先,我创建了一个名为remotely1 的执行脚本:

#!/bin/bash

# Usage:
# Put this thing as a shebang into your scripts
# i.e.:
# 
#   #!/bin/remotely [email protected]
#   
#   echo "hello world from ${hostname}"
#

login=$1
script=$2

args=${@#$1}
args=${args#$2}

tar cz $script | ssh $login "tar xz && bash --login $script $args"

然后在应该远程执行的脚本中使用它:

#!/bin/remotely [email protected]

echo "hello world from ${hostname}"

它还可以通过使用类似的东西来调整以支持多个主机gnu-parallel

答案2

在这里回答我自己的问题。

我们现在有这样的函数:

function remote::run() {
  # usage: remote::run "host" "includes" "commands"
  # where "includes" is a list of functions to export to
  # the remote host

  [[ -n "$2" ]] && includes="$(declare -f $2);"
  ssh -T $1 "$includes $3"
}

这使我们能够做类似的事情(人为的例子):

function status::report() {
  date
  host $(hostname)
}

remote::run $REMOTE_HOSTNAME status::report 'echo status report:; status::report' > out

虽然远非完美,但比起我们之前的要好一些:)

上一个答案:

很难遵循上述脚本中的控制流,我想我可以决定采用如下方法:

#!/bin/bash

# Generate the foo report

# source the shared code/vars if we're running locally
[[ -f shared.sh ]] && source shared.sh

if [[ "$report_host" != "$(hostname)" ]]; then
    # ssh-agent will provide passwordless logon
    runner="ssh -T -o SendEnv=report_host $report_host"
else
    # run report locally
    runner="bash"
fi

report="$($runner << EOF
    $(declare -f run_report)
    run_report
EOF
)"

echo "$report" | mail -s "daily foo report" root

我并不赞成将远程代码放在 heredoc 中。只要它仍然是几个声明,然后是一个简单的函数调用,我认为就可以了。

相关内容