我有时想获取在远程主机上运行的 docker 容器的环境。为此,我登录主机:
ssh kramer65@123.456.789.10
Run Code Online (Sandbox Code Playgroud)
然后我运行这个命令:
sudo docker exec -it `sudo docker ps | grep mycontainername | awk '{print $1;}'` env
Run Code Online (Sandbox Code Playgroud)
我现在想用一个命令来完成此操作(执行超过 3 次,我想自动化它..:-))。
这有效:
ssh -t kramer65@123.456.789.10 sudo docker ps | grep mycontainername
Run Code Online (Sandbox Code Playgroud)
但当我这样做时
ssh -t kramer65@123.456.789.10 sudo docker exec -it `sudo docker ps | grep mycontainername | awk '{print $1;}'` env
Run Code Online (Sandbox Code Playgroud)
我明白了
"docker exec" requires at least 2 arguments.
See 'docker exec --help'.
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Connection to 123.456.789.10 closed.
Run Code Online (Sandbox Code Playgroud)
有谁知道我怎样才能让它运行?
Kam*_*ski 14
Bash 中有一些关键字会影响对其后面内容的解析,例如[[
. 但这ssh
不是其中之一,它是一个常规命令。这意味着:
整行通常由本地shellssh \xe2\x80\xa6
解析;像、、、、或空格这样的字符对 shell 来说意味着一些东西,除非您引用或转义它们,否则它们不会到达。(除了少数例外,例如,sole作为单独的单词并不特殊)。这是解析和解释的第一层次。|
;
*
"
$
ssh
$
ssh
shell 完成工作后,无论参数(或任何其他常规命令)得到什么,它们都只是参数、字符串。现在工具的工作就是解释它们。这是第二个层次。
如果ssh
其某些(零个、一个或多个)命令行参数被解释为要在服务器端运行的命令。一般来说,ssh
能够从许多参数构建命令。效果就好像您在服务器上调用了类似的东西:
"$SHELL" -c "$command_line_built_by_ssh"\n
Run Code Online (Sandbox Code Playgroud)\n(我并不是说它完全像这样,但它确实足够接近以理解发生的情况。我编写它就像在 shell 中调用它一样,所以它看起来很熟悉;但实际上有还没有 shell。并且没有$command_line\xe2\x80\xa6
作为变量,我只是使用这个名称来引用某个字符串以达到这个答案的目的。)
然后$SHELL
在服务器上$command_line\xe2\x80\xa6
自行解析。这是第三个层次。
失败的命令
\n\n\nRun Code Online (Sandbox Code Playgroud)\nssh -t kramer65@123.456.789.10 sudo docker exec -it `sudo docker ps | grep mycontainername | awk \'{print $1;}\'` env\n
失败,因为123.456.789.10
不是有效的 IP 地址。
好的,我知道123.456.789.10
这是一个占位符,但它仍然无效。:)
该命令失败,因为它sudo docker ps | grep mycontainername | awk \'{print $1;}\'
在本地执行。输出可能是空的。那么$command_line_built_by_ssh
离你想要的还很远。
注意在本地ssh \xe2\x80\xa6 | grep mycontainername
运行grep
(您可能会或可能不会意识到这一点)。
为了控制远程 shell 将获得的内容$command_line_built_by_ssh
,您需要理解、预测和策划之前发生的解析和解释。您需要制作本地命令,因此在本地 shell 并消化它之后,它就成为您想要在远程端执行的ssh
确切命令。$command_line\xe2\x80\xa6
如果您确实希望本地 shell 在结果到达之前扩展或替换任何内容,则可能会非常复杂ssh
。您的情况更简单,因为您已经拥有所需的逐字字符串$command_line_built_by_ssh
。该字符串是:
sudo docker exec -it $(sudo docker ps | grep mycontainername | awk \'{print $1;}\') env\n
Run Code Online (Sandbox Code Playgroud)\n笔记:
\n$()
,而不是反引号。有理由选择$()
。docker
我根本不知道,我不知道你是否$(\xe2\x80\xa6)
应该用双引号引起来。一般来说,不引用几乎总是不好的。问问自己,当替换返回多个单词(即多行 Enter awk
)时会发生什么。这是一个不同的问题(如果在这种情况下有问题的话),我不会在这个答案中解决它。为了保护所有内容不被本地 shell 扩展/解释,您需要正确引用或转义(使用\\
)所有可以触发扩展或可以解释的字符。在这种情况下$
,引用(
、)
、|
、;
、{
、}
、\'
和(可能或可选)空格。
我说“也许或可选地引用或转义空格”是因为它的ssh \xe2\x80\xa6 some command
工作原理。如果它找到两个或多个参数并将其解释为要在服务器上运行的代码,它将连接它们,并在它们之间添加单个空格。就是这样$command_line_built_by_ssh
建造的。如果您在看起来像远程 shell 的代码中既不引用也不转义空格,则本地 shell 在分割单词时将消耗空格(和制表符),然后ssh
添加空格。如果存在制表符或多个连续空格,结果可能不完全是您想要的。例如:
ssh user@server echo a b\n
Run Code Online (Sandbox Code Playgroud)\nssh
得到user@server
, echo
, a
, b
。远程命令将是,echo a b
并且echo
将得到a
, b
。它将打印a b
。
然后这个:
\nssh user@server \'echo a b\'\n
Run Code Online (Sandbox Code Playgroud)\nssh
得到user@server
, echo a b
. 远程命令将是,echo a b
并且echo
将得到a
, b
。它将打印a b
。
最后是这个:
\nssh user@server \'echo "a b"\'\n
Run Code Online (Sandbox Code Playgroud)\nssh
得到user@server
, echo "a b"
. 远程命令将会是echo "a b"
并且echo
将会有 get a b
。它将打印a b
。
结论是您应该在本地 shell 上下文中引用,并在远程 shell 上下文中单独引用。请记住,当涉及到通过 shell 扩展内容时,外部引号很重要。
\n将所有这些信息放在一起(并且仍然假设您希望保护所有内容不被本地 shell 扩展/解释),我建议如下:
\n\\
引号只能保护一个字符。您很可能需要许多反斜杠来保护所有内容;通常只需一对引号即可达到相同的结果。\'
),它们可以保护除 之外的所有内容\'
。另一方面,双引号 ( "
) 可以保护\'
但不能保护$
nor "
(在 Bash 中\\
有时也不能,有时也不能),除非和此类也被转义(即转义和引用,即在双引号内转义;除非这很麻烦)。!
$
!
ssh
.这导致以下过程:
\n\'
为 或\'"\'"\'
with \'\\\'\'
(您可以为每个 单独选择\'
)。ssh \xe2\x80\xa6
在前面。您的逐字命令是:
\nsudo docker exec -it $(sudo docker ps | grep mycontainername | awk \'{print $1;}\') env\n
Run Code Online (Sandbox Code Playgroud)\n该程序的结果是:
\nssh -t user@server \'sudo docker exec -it $(sudo docker ps | grep mycontainername | awk \'\\\'\'{print $1;}\'\\\'\') env\'\n# single-quoted ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^\n# escaped ^ ^\n
Run Code Online (Sandbox Code Playgroud)\n请注意,该过程可能会也可能不会导致最短的字符串。凭借洞察力,有时可以“优化”字符串。但该过程非常简单且完全可靠。如果您知道要保护所有内容不被本地 shell 扩展/解释,则该过程本身根本不需要进一步了解。
\n事实上,该过程可以自动化。有些工具可以向字符串添加引号并正确保留现有引号。Bash 本身就是这样的工具。我的这个答案提供了一种在 Bash 中通过击键来做到这一点的方法。根据您的情况调整的可能解决方案是:
\n在本地 shell 中定义以下自定义函数和绑定:
\n _prepend_ssh() { READLINE_LINE="ssh ${READLINE_LINE@Q}"; READLINE_POINT=4; }\n bind -x \'"\\C-x\\C-h":_prepend_ssh\'\n
Run Code Online (Sandbox Code Playgroud)\n仍然在本地 shell 中键入(或粘贴)要在远程 shell 中运行的命令;不执行。该命令应该与您希望在远程 shell 中显示的命令完全相同。
\n按Ctrl+ x、Ctrl+ h。本地 shell 将负责引用。它还会ssh
在前面添加并将光标放在后面。
添加(类型)缺少的参数(例如-t user@server
)。为了您的方便,光标已经处于执行此操作的正确位置。
Enter
\n还有另一种方法可以将逐字命令传递到远程 shell。在某些情况下,您可以通过管道它们ssh
。让我们假设远程命令行应该是:
echo "$PATH"; date\n
Run Code Online (Sandbox Code Playgroud)\n我们可以像上面一样继续,添加单引号并在本地运行,如下所示:
\nssh user@server \'echo "$PATH"; date\'\n
Run Code Online (Sandbox Code Playgroud)\n该示例很简单,但通常添加引号并不总是那么容易。或者,我们可以像这样通过管道传输命令(echo
为了简单起见,第一个printf
更好):
echo \'echo "$PATH"; date\' | ssh user@server bash\n
Run Code Online (Sandbox Code Playgroud)\n仍然需要这些单引号。但是如果文件中有命令,那么:
\n<file ssh user@server bash\n
Run Code Online (Sandbox Code Playgroud)\n或者甚至没有任何文件(此处为文档):
\nssh user@server bash <<\'EOF\'\necho "$PATH"\ndate\nEOF\n
Run Code Online (Sandbox Code Playgroud)\n<<\'EOF\'
(请注意防止$PATH
在本地展开的引号。)
优点:
\necho \xe2\x80\xa6 ; date
只是为了展示这一点)。bash
或zsh
、 或python
)。缺点:
\nexec "$SHELL"
(该行将类似于ssh \xe2\x80\xa6 \'exec "$SHELL"\' <<\'EOF\'
)。ssh
不是终端,因此您无法使用-t
(这就是为什么我没有使用您的原始命令作为示例)。bash
通过其标准输入到达远程解释器(在示例中)。可能的问题:\n 归档时间: |
|
查看次数: |
5714 次 |
最近记录: |