shell 通过ssh和zsh -lc进行算术扩展会引发分析错误,

jdg4fx2g  于 2023-04-07  发布在  Shell
关注(0)|答案(2)|浏览(141)

我正在尝试以下操作:

❯ ssh user1@10.8.1.1 zsh --version
zsh 5.8.1 (x86_64-apple-darwin22.0)

❯ ssh user1@10.8.1.1 zsh -lc "for ((index=1; index <= 1; index++)); do echo \$index; done"
zsh:1: parse error near `)'

然而,在本地,也没有zsh -lc通过ssh,它的工作原理是:

❯ zsh -lc "for ((index=1; index <= 1; index++)); do echo $index; done"
1
❯ ssh user1@10.8.1.1 "for ((index=1; index <= 1; index++)); do echo \$index; done"
1

当我在for循环之前做任何事情时,我也可以让它工作:

❯ ssh user1@10.8.1.1 zsh -lc "echo 1; for ((index=1; index <= 1; index++)); do echo \$index; done"

1

有什么建议我该去哪里找吗?

pb3s4cty

pb3s4cty1#

ssh只向远程shell传递一个字符串。不是单个参数的列表--一个字符串。当你传递多个参数时,它只是用空格将它们连接起来以生成该字符串;因此,当您自己生成一个字符串时(将zsh-lc都作为该字符串的一部分),它可以更容易地考虑这一点,从而得到正确的结果。
下面的两种方法都避免了手动转义的需要:作为一个人,你永远不会知道你需要将$index更改为\$index,或者决定如何重新转义它以通过额外的shell(无论是本地的还是远程的)。

确保 * 只有 * zsh可以看到/解析/munge你传递的代码

如果你不知道默认的远程shell是什么,并且你正在运行的代码没有从stdin读取,理想的方法是在stdin上传递代码,这样它就只在zsh的远程副本启动之后才通过线路:

# define a function so we can use declare -f to serialize the code
rmtfunc() { for ((index=1; index <= 1; index++)); do echo $index; done; }

# make the remote shell start zsh before code is passed to it
ssh user1@10.8.1.1 zsh -l <<EOF
$(declare -f rmtfunc); rmtfunc
EOF

这样,您还可以使用$(declare -p varname)通过网络传递局部变量,并确保它们的值由zsh* 解析,而不需要任何其他shell。

如果远程shell足够兼容,则可以使用一个保持stdin清晰的回退

# declare a function with the code we want to run remotely
rmtfunc() { for ((index=1; index <= 1; index++)); do echo $index; done; }

# use printf %q to serialize that into a string
printf -v ssh_cmd '%q ' zsh -lc "$(declare -f rmtfunc); rmtfunc"

# pass that string as an argument to ssh
ssh user1@10.8.1.1 "$ssh_cmd"

如果远程shell是带有ksh扩展的任何东西--不仅仅是zsh,还有bash &c --这都可以正常工作,但如果printf %q使用$'\t'$'\n'语法转义制表符或换行符,则可能会失败。

解释错误所在

当你跑的时候...

ssh user1@10.8.1.1 zsh -lc "for ((index=1; index <= 1; index++)); do echo \$index; done"

首先,引号被 * 本地 * 删除,然后ssh连接参数生成一个字符串传递给远程shell,所以我们得到:

zsh -lc for ((index=1; index <= 1; index++)); do echo $index; done

在这里,只有for作为-c的参数给出;接下来,我们有后续的命令进入zsh的argv,然后是shell语法,远程shell会感到困惑/惊讶。

falq053o

falq053o2#

还有一个你想避免的目标shell的单词分割。所以再次引用应该可以:

ssh user1@10.8.1.1 "zsh -lc 'for ((index=1; index <= 1; index++)); do echo \$index; done'"

相关问题