当使用 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
现在scp
用spy
而不是 来调用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
运行服务器。正确的命令作为单个字符串传递给。它始终是(其中包括特殊选项)。它是的最后一个参数。scp
ssh
scp …
…
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-helper
container
scp
scp
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
可以在两个远程位置之间进行复制(有或没有)-3
)balena-helper
不适用于此模式。重建最后一个参数时,
balena-helper
用引号 (@Q
) 括住几个字符串,这样当远程主机上的 shell 解释整个命令字符串时,它们就无法注入代码。scp …
容器中到达 shell 的命令能运行额外的代码;但这是scp
正常情况下可以做的(修复它不是我的本意)。例如,这个命令使我的 Debian 10 发出哔哔声:scp kamil@debianserver:"; beep" ./
它之所以有效,只是因为我无论如何都可以运行
beep
viassh
。scp
这并没有给我(或任何其他人)更多的可能性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