如何 scp 名称中带有“$1”的 java 内部类

如何 scp 名称中带有“$1”的 java 内部类

文件

sample$1.class
sample.class
scp_test.sh

scp_测试.sh

#!/bin/bash

TARGET=('sample.class' 'sample$1.class')

for dd in "${TARGET[@]}"
do
  FILENAME=`basename ${dd}`
  scp ${FILENAME} remote:/tmp/${dd}
done

当 shell 运行时,会在远程服务器上sample$1.class被覆盖, 只留下 。sample.class
sample.class

$ ./scp_test.sh
sample.class               100%    0     0.0KB/s   00:00
sample$1.class             100%    0     0.0KB/s   00:00
$ ssh remote 'ls -l /tmp/'
total 01 
sample.class

仅当通过 复制时才会发生这种情况scp。本地复制使用cp不会出现这种情况。


*编辑由于我使用的 Bash 版本低于 4.3(4.2.46),因此无法使用。我通过使用引号组合而不是普通的 来${dd@Q}解决问题。'"${dd}"'${dd}

答案1

初步说明

这个答案主要涉及传统的,即使用 SCP 协议的scp传统。OpenSSH的现代版本默认使用 SFTP,当我写这篇文章时,转换还不到两个月;所以它很新。我可以告诉你你使用的是 SCP,因为如果它使用 SFTP,你就不会遇到这个问题。解决这些问题是 SCP 被 SFTP 取代的原因之一。scpscpscp

来自变更日志

OpenSSH 9.0 于 2022-04-08 发布。 […]

[…]

此版本scp(1)默认从使用旧的 scp/rcp 协议切换到使用 SFTP 协议。

旧版 scp/rcp 通过远程 shell 对远程文件名执行通配符扩展(例如 scp host:* .)。这有副作用,即要求对scp(1)命令行中包含的文件名中的 shell 元字符使用双引号,否则它们可能会在远程端被解释为 shell 命令。


独立于以下方面的潜在问题scp

首先:引用正确。即使不加引号,您的示例名称也可能是安全的,编写健壮的代码仍然是一种美德。在我看来,如果您可以不加引号,那么始终加引号比每次都费力思考要容易得多。带有正确加引号的变量的代码将如下所示(请注意,我暂时不会尝试解决这个问题scp,稍后会解决):

#!/bin/bash

target=('sample.class' 'sample$1.class')

for dd in "${target[@]}"
do
  filename="$(basename "${dd}")"
  scp -- "${filename}" "remote:/tmp/${dd}"
done

(我也修复了名字全部大写,引入双划线万一您将以破折号开头的文件名添加到target。我认为basename在这种特殊情况下是无操作的,但我认为您有这样做的理由。)

如果您scp使用 SFTP,那么上述代码将会起作用(坦率地说,我认为您的原始代码也可以使用这样的代码scp,但仅仅是因为您使用的名称在未加引号时是“安全的”)。


问题scp

不幸的是,旧版scp将远程路径名嵌入到了 shell 代码中,而该代码是要由远程 shell 来解释的(比较我的这个答案)。远程 shell 将解释引号、 等字符$[除非它们在到达远程 shell 时被转义或引用。这意味着您需要在本地此外考虑远程 shell 的引用。您需要为本地 shell 引用用于远程 shell。这就是引文中“要求在文件名中对 shell 元字符使用双引号”的含义。


解决方案

Bash 可以提供帮助。"${dd@Q}"将扩展dd并转义或引用结果,因此经过进一步的解释(在您的情况下由远程 shell 执行)结果将是您期望的单个单词(如"$dd"本地扩展)。以下行是对旧版的修复scp

scp -- "${filename}" "remote:/tmp/${dd@Q}"

整个脚本如下:

#!/bin/bash

target=('sample.class' 'sample$1.class')

for dd in "${target[@]}"
do
  filename="$(basename "${dd}")"
  scp -- "${filename}" "remote:/tmp/${dd@Q}"
done

该解决方案不可移植,它无法在纯 . 中工作sh。脚本中的 shebang 是#!/bin/bash从头开始的,所以我想你不介意只在 Bash 中工作的代码。


最后说明

  • 第一个代码片段(不带${dd@Q})适用于scp使用 SFTP(即新版本)。第二个代码片段(带${dd@Q})适用于scp使用 SCP(即旧版本scp)。没有简单的通用代码。新版本scp支持-O,使其行为类似于旧版本,但如果您使用 带的scp旧版本,则它会失败,因为它会发现选项无效。无论哪种方式,您都需要知道您的是新的还是旧的,并相应地调整您的 shell 代码。目前您的是旧的(因此首先存在问题),但如果您更新它,它可能会被新的替换。已经链接的scp-Oscpscp变更日志注意到不兼容性:

    这会产生一个潜在的不兼容性:使用 SFTP 协议时不再需要这种繁琐而脆弱的引用,尝试使用它可能会导致传输失败。我们认为删除文件名中双引号 shell 字符的需要是一个好处,并且不打算在使用 SFTP 协议时scp(1)引入对旧版 scp/rcp 的错误兼容性。scp(1)

  • 侧面“问题”,一个友好的建议。将脚本命名为scp_test.sh不是一个好习惯。对于您的scp_test.sh解释器来说,已经是bash,而不是sh。您的原始代码需要bash并且无法在纯环境中工作sh(因为数组)。您要重命名为 吗scp_test.bash?如果您将脚本移植到 Python 会怎样?好的,我认为您不会对非常脚本有问题,但一般来说,您可能会遇到这种情况。然后,每个调用的工具scp_test.sh都需要修复并scp_test.py改为调用。

    将脚本命名为scp_test。您可以scp_test通过检查其权限来判断它是否可执行。如果您想知道它是什么,请调用file scp_test。现在您可以用任何您想要的语言重写脚本,甚至可以编译二进制文件,而您(或任何人/任何东西)仍然可以将其作为 运行scp_test

相关内容