为什么 bash -c 'cd / ; pwd' 通过 SSH 运行时失败

Pio*_*pla 1 linux ssh bash ubuntu

我试图找出为什么以下命令无法正确设置工作目录。

ssh localhost bash -c "cd / ;pwd"
Run Code Online (Sandbox Code Playgroud)

虽然以下版本有效:

bash -c "cd / ;pwd"

ssh localhost "cd / ;pwd" 

ssh localhost 'bash -c "cd / ;pwd"'

ssh localhost bash -c "pwd; cd / ;pwd"
Run Code Online (Sandbox Code Playgroud)

我在 macOS 和 ubuntu 18.04 上注意到了这种行为。

Kam*_*ski 5

初步说明

\n

这里我将使用“本地”“远程”这两个词来分别区分发生在 SSH 客户端和 SSH 服务器端的情况。localhost忘记你的 SSH 服务器是“本地”这一事实;将其视为巧合。一般来说,SSH 服务器是远程的。

\n
\n

分析

\n

ssh能够根据您提供的多个参数构建用于远程执行的代码,但此代码不会作为数组传递。它作为单个字符串传递。参数是否正确并不重要

\n
    \n
  • bash, -c,cd / ;pwd
  • \n
  • bash, -c, cd, /,;pwd
  • \n
  • bash -c, cd,/ ;pwd
  • \n
  • bash -c cd / ;pwd
  • \n
\n

每种情况下得到的字符串都是bash -c cd / ;pwd. 该字符串将由远程 shell(由 SSH 服务器生成的 shell)解释。因为;远程 shell 会识别两个命令。它将把它们分割成单词,如bash, -c, cd, /; 然后(作为单独的命令)pwd。这将运行bash执行cd(注意:甚至不是cd /),它不能更改父 shell 的状态,因此它是一个无操作(在某些奇怪的设置中,它可能会抛出错误,但仍然是一个无操作);然后pwd完全独立于之前的命令。

\n
\n

解决方案

\n

您要使用的字符串ssh与上面的不同,它是bash -c "cd / ;pwd". 要获得它,您需要使双引号在本地 shell(您在其中键入命令的位置ssh \xe2\x80\xa6)的引号删除阶段中幸存下来。您需要引用引号或转义它们。带引号或转义的引号不能防止;被本地解释为命令终止符/分隔符,因此也必须带引号或转义:

\n
ssh localhost \'bash -c "cd / ;pwd"\'   # this one you have already discovered\n#or\nssh localhost bash -c \\"cd / \\;pwd\\"\n
Run Code Online (Sandbox Code Playgroud)\n

(这些并不是生成所需远程字符串的唯一可能的本地命令。)

\n

请注意,在前一种情况下,只有一个参数可以构建结果字符串;在后一种情况下,字符串是由ssh多个本地参数创建的。当我需要引用或转义任何内容时,我更喜欢精心设计一个论点。如果所需的远程命令是静态的(我的意思是如果它不依赖于本地扩展,即本地变量等),那么制作这样的本地参数是算法性的,并不那么困难,它可以在 Bash 中自动化

\n
\n

边注

\n

运行bash -cviassh可能是不必要的。无论您ssh构建什么字符串,它都会被远程端的某个 shell(用户的登录 shell)解释。shell 可能是也可能不是 Bash。如果您故意希望 Bash 解释某些代码(例如,因为代码包含bashisms),那么这bash -c是有用的。能够运行的 shellbash -c \xe2\x80\xa6应该能够cd / ;pwd按照您的预期进行解释,因此在这种情况下bash -c不需要。

\n

你尝试过ssh localhost "cd / ;pwd"并且成功了。它传递cd / ;pwd到您的远程 shell,无论是否是 Bash。它比带有bash -c.

\n

我的理解cd / ;pwd只是一个例子。一般来说,代码可能需要 Bash。OTOH 一般来说,它可能包含引号。我的观点是有两三个 shell,每个 shell 都会解释引号等;他们是:

\n
    \n
  1. 产卵前的本地壳ssh
  2. \n
  3. SSH 守护进程生成的远程 shell
  4. \n
  5. bash -c在这种情况下bash
  6. \n
\n

堆叠的 shell 数量越多,需要的引用和/或转义就越复杂。bash -c这是有代价的:它增加了本地命令的复杂性。如果您知道远程端的登录 shell 可以正确解释您要运行的代码,那么bash -c很可能只是一个负担。

\n