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

Dan*_*scu 10 bash ssh wildcards

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

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

$ ls file-*
$ bash -c 'ls file-*'
Run Code Online (Sandbox Code Playgroud)

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

$ ssh remote bash -c 'ls file-*'
$ ssh remote bash -c 'ls "file-*"'
$ ssh remote bash -c 'ls file-\*'
$ ssh remote bash -c 'ls "file-\*"'
Run Code Online (Sandbox Code Playgroud)

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

Kus*_*nda 14

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

bash -c ls file-*
Run Code Online (Sandbox Code Playgroud)

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

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

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

[...]
debug1: Sending command: bash -c ls file-*
[...]
Run Code Online (Sandbox Code Playgroud)

其他三个命令你展示的作品相同,但将只设置$0为字符串file-*,而不是设置$1$2等的bash -c外壳。

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

ssh remote 'bash -c "ls file-*"'
Run Code Online (Sandbox Code Playgroud)

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

[...]
debug1: Sending command: bash -c "ls file-*"
[...]
Run Code Online (Sandbox Code Playgroud)

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

你也可以用

ssh remote bash -c \"ls file-\*\"
Run Code Online (Sandbox Code Playgroud)

或者

ssh remote bash -c '"ls file-*"'
Run Code Online (Sandbox Code Playgroud)