为什么 ls 参数似乎在通过 ssh 主机 bash -c 运行的命令中不起作用?

为什么 ls 参数似乎在通过 ssh 主机 bash -c 运行的命令中不起作用?

我有一个本地主机和一个远程主机,都运行 Ubuntu,shell 设置为 bash。在我的主目录中,我有两个文件file-1file-2,分别位于本地主机和名为 的远程主机上remote。每个主目录中还有一些其他文件,我只想列出匹配的文件file-*

在本地,这些会产生预期的结果file-1 file-2

$ ls file-*
$ bash -c 'ls file-*'

但这些命令返回远程主目录中的所有文件。那里发生了什么事?

$ ssh remote bash -c 'ls file-*'
$ ssh remote bash -c 'ls "file-*"'
$ ssh remote bash -c 'ls file-\*'
$ ssh remote bash -c 'ls "file-\*"'

我知道这只会ssh remote 'ls file-*'产生预期的结果,但为什么似乎ssh remote bash -c 'ls ...'放弃了传递给 的参数ls ...? (我还通过管道传输了远程执行的 ls 的输出,并且它被传递,因此ls似乎只有 受到影响:ssh remote bash -c 'ls file-* | xargs -I {} echo "Got this: {}"'。)

答案1

使用时在远程主机上执行的命令ssh remote bash -c 'ls file-*'

bash -c ls file-*

这意味着bash -c执行脚本ls。作为位置参数,bash -c脚本获取远程主机上匹配的名称file-*(这些名称中的第一个将被放入 中$0,因此它实际上并不是位置参数的一部分)。参数不会传递给ls命令,因此会列出目录中的所有名称。

ssh在远程主机上传递要执行的命令,并删除一级引号(在命令行上使用的外部引号集)。您调用的 shellssh会删除这些引号,并且ssh不会插入新的引号来分隔远程命令的参数(因为这可能会干扰命令使用的引号)。

如果您使用以下命令,您可以看到这一点ssh -v

[...]
debug1: Sending command: bash -c ls file-*
[...]

您显示的其他三个命令的工作方式相同,但只会设置$0为字符串file-*,而不为 shell 设置$1$2bash -c

您可能想要做的是引用整个命令:

ssh remote 'bash -c "ls file-*"'

ssh -v调试输出中,报告为

[...]
debug1: Sending command: bash -c "ls file-*"
[...]

简而言之,您必须确保作为远程命令传递的字符串是您要在本地 shell 删除引号后运行的命令。

你也可以使用

ssh remote bash -c \"ls file-\*\"

或者

ssh remote bash -c '"ls file-*"'

答案2

我认为误解是您希望 ssh 参数直接由服务器执行,但事实并非如此。当你写:

ssh remote bash -c 'ls file-*'

ssh 客户端会将bash-cls file-*、用空格连接起来bash -c ls file-*并将其发送到服务器。服务器将获取该字符串,并将其作为一个参数传递给bash -c(如果这是您的远程 shell)。这意味着您最终会在服务器中执行与以下内容等效的操作:

bash -c 'bash -c ls file-*'

strace您可以通过在ssh 服务器上执行命令来验证这一点:

$ sudo strace -fe trace=execve -p "$(pgrep -o sshd)" |& grep bash
[pid 794417] execve("/bin/bash", ["bash", "-c", "bash -c ls files-*"], 0x560ab66915d0 /* 13 vars */) = 0

我想你想要的是:

ssh remote 'ls file-*'

以便服务器执行:

bash -c 'ls file-*'

相关内容