sed 不会作为 ssh 远程命令执行

sed 不会作为 ssh 远程命令执行

我正在编写一个脚本,该脚本允许我通过 ssh 从服务器中删除旧公钥。问题似乎出在我的 ssh 命令的格式上:

ssh -qo ConnectTimeout=20 "${user_ssh}@${ip}" "sed -ein \"s|ssh-rsa\s${fingerprint}\s\(.*\)|#---> deleted by public-key-remover.sh. USER:\1|g\" $key_dir || exit 67"

$fingerprint 是 rsa 公钥的指纹。

$key_dir 是公钥文件的位置。

我确保 $key_dir 确实存在但它仍然打印出以下错误:

sed: can't read s|ssh-rsa\sAAAAB3NzaC1yc2EAAAADAQABAAABAQCDlMyH7CX5ckyspCDx8pqzLv41OM0xjlrSeHhi8gz+6jXepaWDtcrlGzkieN3oOK+uUJKT5Jz8uSRBoTfr20tTLLHklRASoWiy7EvACwqfClRVnC67KBWKWlsIDLBGC3KDkJQbYD8ovDP6svy7xBMW0J1RWnQW/RqaCZ9udFMzh5KUbCCm360eVNnFq7fAR//ZeUHNlTGFfSl/WfxkjacAAfKGjtnxYHfo8N/IGP9rbGoX/nlU8XOr2y9uHkVHt+ghG8/wnZo1quUua8JbjMOv6Ygu40Qe9E5pXKOZr1v1RkvMrNOoZ9O8KNvVt7L5+0TvM2NG7AK51qo9ZpHNPr2b\s\(.*\)|#---> deleted by public-key-remover.sh. USER:\1|g: No such file or directory

由于我对 Bash 还很陌生,所以我需要一些帮助。

答案1

主要问题

这里的主要问题是,-einsed -ein …是一个需要选项参数(要执行的 sed 脚本)的选项。in恰好是一个有效的 sed 脚本,并被当作有效脚本。 随后的内容将被解释为要处理的文件的路径。 就好像远程端的命令是 一样sed -e in …

我不确定这是否是唯一的问题,但我确信这就是你sed被解释s|ssh-rsa…为路径的原因。

如果您想要以紧凑的形式传递-n和选项,那么必须是最后一个,因为它需要一个选项参数(不需要)。选项参数(即您的 sed 脚本)必须紧跟在后面:-e-e-n

sed -i -ne …

我将其分开,-i因为-isomething具有含义(可能与不同-i something)并且您显然不希望它在这里,您想要-i

一种直接的方法是逐个指定选项:

sed -i -n -e …

但我真的怀疑你是否想要-n。你的 sed 脚本没有明确打印任何内容,因此 with-n不会有任何效果。这意味着-i -n你的 sed 脚本将清除文件的内容。

注意,如果您尝试了sed -e -i …sed则会抱怨-这是未知命令,这与您所得到的错误完全不同。这是因为-i不是有效的 sed 脚本,而您in意外地 是。


更广阔的前景

我们在这里可以观察到一些惯例:

  1. -n选项sed既不需要也不接受选项参数,另一个选项可以跟n在以 开头的单个参数中-sed -nwhatever相当于, 的含义取决于 的sed -n -whatever含义。对于不需要选项参数的选项来说, 的约定很常见。-whatever-wsed-abc-a -b -c

  2. -e选项sed需要选项参数。sed -ewhatever相当于sed -e whatever。 对于需要选项参数的选项,这种约定相当常见。

  3. -iGNU 的选项sed可以接受也可以不接受选项参数。当您想要指定一个时,您需要将其与 连接起来-i-iwhatever不等同于-i whatever也不等同于-i -whatever。这种约定并不常见。请注意 选项不可移植。sed您使用的 可能是也可能不是 GNU sed。比较此线程:sed -i空参数兼容性问题

    不幸的是,这是 GNU sed 扩展,其使用本质上不是可移植的。FreeBSD 的使用与此-i类似,但略微不兼容。POSIX-i未指定该选项,因此根据定义,对它的任何使用都是非标准的。

    -n并且-e由 POSIX 指定

可能还有其他惯例。不幸的是,手册很少有助于了解哪种工具遵循这种类型的惯例。通常,遵循常见惯例的好工具不会费心(在手册中)说明它们遵循惯例,可能是因为“每个人都应该遵循,这是显而易见的”。而不太好的工具通常不会费心说明它们不遵循,因为它们不在乎;如果他们在乎,他们首先就会遵循。这是我的印象,我的猜测。然后有些工具甚至不遵循以破折号开头选项的惯例(出于各种原因)。

man sed我的观点是,你可以从、man ls或中学习相应的选项,但你可以做或的man du事实并没有明确记录ls -hlsdu -hms那里. 了解常见惯例,尝试使用对您来说很新的工具。许多人会遵循,有些人可能不会(或不会完全遵循)。如有疑问,请将选项指定为单独的参数。


边注

的某些值$key_dir会有问题。请记住,远程 shell 将再次解释整个字符串(如果$key_dir扩展为带有空格的字符串会怎样?或者带有????")。如果您的本地 shell 是 Bash,则使用。Bash 将以特殊方式扩展变量,因此在(远程)shell 中解析结果后,它会再次变为原始字符串。扩展的字符串被正确引用和/或转义。'$(reboot)${key_dir@Q}

答案2

sed选项-i采用备份文件的扩展名,因此不要将该选项与其他选项捆绑在一起。

另外,-n停止 sed 打印行,因此您最终会得到一个空文件。

嵌入的反斜杠应当被转义。

-E选项意味着括号不需要转义。

尝试一下(使用额外的换行符来增加可读性)

ssh -qo ConnectTimeout=20 "${user_ssh}@${ip}" "
    sed -i -E 's|ssh-rsa\\s${fingerprint}\\s(.*)|#---> deleted by public-key-remover.sh. USER:\\1|g' '$key_dir' || exit 67
"

答案3

询问 Ubuntu,回答者哈维· M.

idssh=$(awk '{print $2}' ~/.ssh/id_rsa.pub)
ssh $remote "sed -i '\#$idssh#d' .ssh/authorized_keys"

相关内容