通过 SSH 在服务器中运行脚本

通过 SSH 在服务器中运行脚本

当使用 ssh 访问运行 balenaos 的 raspberry pi 时,ssh -t -p 22222 root@device-ip "balena-engine exec -it <container_name> /bin/sh"授予我访问 balenaos 主机内特定容器的权限。

现在我需要使用 scp 将文件传输到这个特定的容器。正确的语法是什么,才能使 scp 以与上述 ssh 命令相同的方式连接到此容器?

答案1

初步说明

这个答案是在 OpenSSHscp默认使用 SFTP 之前写的(2022-04-08,参见变更日志)答案调查并依赖于遗产 scp请参阅“历史、SCP 和 SFTP”我的另一个答案


分析

要将scp某些东西放入容器中,您需要能够scp在容器中运行。不过,您不需要容器中的 SSH 服务器。

如果您无法scp在容器中运行,那么这个答案将对您没有帮助。

当您scp往返于远程位置时,您的本地scp(客户端)会ssh在后台运行以连接到远程位置并在那里运行特定命令。该命令以单个字符串的形式传递,并由远程用户的 shell 进行解释,就像通过 传递的任何命令一样ssh

如果scp命令是… scp;但是带有特殊选项。这些选项未记录(您不会在中找到它们man 1 scp),它们会scp变成服务器。

如果您设法改变传递给远程 shell 的字符串,那么您将能够scp在容器中而不是在主机中运行服务器(假设您实际上可以scp在容器中运行)。

-S您可以通过以下选项改变字符串scp

-S program
用于加密连接的程序名称。该程序必须理解ssh(1)选项。

来源

-S可以使用 来scp运行自定义可执行文件,而不是ssh。然后可执行文件应该运行ssh并将更改后的字符串传递给它。


引擎盖下发生了什么

(您可以跳过此部分。它仅用于教育目的。)

让我们看看究竟scp调用了什么以及未记录的选项是什么。创建一个名为的本地文件spy并使其可执行(chmod +x spy)。这是要输入的代码spy

#!/bin/sh
printf '<%s> ' "$@" >/dev/tty
printf '\n' >/dev/tty

现在scpspy而不是 来调用ssh。使用服务器的虚拟名称,我们的spy将不会连接任何东西。

  • 正在下载。本地命令:

    scp -S ./spy dummy:/remote/path /local/path
    

    输出自spy

    <-x> <-oForwardAgent=no> <-oPermitLocalCommand=no> <-oClearAllForwardings=yes> <--> <dummy> <scp -f /remote/path>
    
  • 正在上传。本地命令:

    scp -S ./spy /local/path dummy:/remote/path
    

    输出自spy

    <-x> <-oForwardAgent=no> <-oPermitLocalCommand=no> <-oClearAllForwardings=yes> <--> <dummy> <scp -t /remote/path>
    
  • 上传多个文件。本地命令:

    scp -S ./spy /local/path1 ./path2 dummy:/remote/path
    

    输出自spy

    <-x> <-oForwardAgent=no> <-oPermitLocalCommand=no> <-oClearAllForwardings=yes> <--> <dummy> <scp -d -t /remote/path>
    

这意味着在没有 shell的情况下spy和在 shell 中(将删除引号)命令将分别如下所示:

ssh -x -oForwardAgent=no -oPermitLocalCommand=no -oClearAllForwardings=yes -- dummy 'scp -f /remote/path'
ssh -x -oForwardAgent=no -oPermitLocalCommand=no -oClearAllForwardings=yes -- dummy 'scp -t /remote/path'
ssh -x -oForwardAgent=no -oPermitLocalCommand=no -oClearAllForwardings=yes -- dummy 'scp -d -t /remote/path'

我们可以看到遥控器的特殊选项scp-f-t-d。您还可以找到它们这里


修改远程命令

如前所述和所示,scp客户端使用在远程端ssh运行服务器。正确的命令作为单个字符串传递给。它始终是(其中包括特殊选项)。它是的最后一个参数。scpsshscp …ssh

scp要在容器中而不是在主机中运行,您需要将此scp …字符串转换为balena-engine exec -it <container_name> scp …。但这有点不妥。请注意这样的本地命令:

scp 'server:/remote/path/*' ./

可用于下载许多文件。通配符 ( *) 应该在远程 shell 中起作用。实际上,远程命令将是scp … /remote/path/*,并且远程 shell 将扩展/remote/path/*。但是,如果将远程命令转换为:

balena-engine exec -it <container_name> scp … /remote/path/*

那么主机上运行的 shell 将展开/remote/path/*。这是错误的,/remote/path/甚至可能不存在于主机中;如果它存在,其内容可能与scp容器中的内容不同。您很可能希望/remote/path/*在容器中展开。为此,您需要容器中的 shell。您需要变成scp …

balena-engine exec -it <container_name> /bin/sh 'scp …'

的最后一个参数ssh应该是这样的。后面隐藏的单引号很麻烦。有办法来处理它们。


修改远程命令 – 脚本

让我们构建一个自定义可执行文件(脚本)并告诉scp使用它而不是ssh。我们的可执行文件将ssh使用修改后的最后一个命令行参数进行调用。

最好不要硬编码<container_name>scp -S采用可执行文件的名称,我们不能通过这种方式传递选项。但我们可以在环境中传递一些东西。

创建一个名为的本地文件balena-helper并使其可执行(chmod +x balena-helper)。这是要放入文件中的代码:

#!/bin/bash
set -- "${@:1:$(($#-1))}" "balena-engine exec -it ${container@Q} /bin/sh ${!#@Q}"
exec ssh "$@"

代码使用了 Bash 的一些不可移植功能。它无法在普通的 中运行sh


用法

一般用法balena-helper如下:

container=container_name scp -S /local/path/to/balena-helper …

其中表示通常会在其后放置的所有选项和操作数scp(例如-P 22222 /local/file root@device-ip:/destination/;请注意,它是-P 22222,而 则ssh-p 22222)。

可以创建一个包装器脚本,当且仅当它在环境中时,它scp才会使用(否则它将表现得像常规脚本)。不过我不会这么做。如果我是你,并且要使用特定容器,我会创建一个临时别名:balena-helpercontainerscpscp

alias scp='container=container_name scp -S /local/path/to/balena-helper'

然后我会scp以一种直接的方式在这个特定的本地 shell 中使用它,例如:

scp -P 22222 /local/file root@device-ip:/destination/

/destination/在容器中哪里有效。最后我会unalias scp


笔记

  • scp可以从本地复制到本地。在这种情况下-S将被忽略。

  • scp可以在两个远程位置之间进行复制(有或没有)-3balena-helper不适用于此模式。

  • 重建最后一个参数时,balena-helper用引号 ( @Q) 括住几个字符串,这样当远程主机上的 shell 解释整个命令字符串时,它们就无法注入代码。scp …容器中到达 shell 的命令运行额外的代码;但这是scp正常情况下可以做的(修复它不是我的本意)。例如,这个命令使我的 Debian 10 发出哔哔声:

    scp kamil@debianserver:"; beep" ./
    

    它之所以有效,只是因为我无论如何都可以运行beepvia sshscp这并没有给我(或任何其他人)更多的可能性ssh。不过,如果我忘记了,我为本地提供的某些参数scp将成为 shell 的一部分代码在远程端,我可能会无意中在scp即将以服务器模式运行的机器(或容器)上运行一些代码。你也有可能。

  • 这个已经提到的答案Bash 中的通知@Q并不完善。请参阅这是另一个答案作为替代方案。我选择@Q保持balena-helper相对简单。

答案2

通过 SSH 在服务器中运行脚本

在本地机器上编写脚本,并使用参数(var1,var2)在服务器中运行它:

var1="test1"  
var2="test2"  
ssh -i ~/.ssh/id_rsa  USER@HOST "bash -s" < $HOME/locale.sh $var1 $var2

相关内容