文件
sample$1.class
sample.class
scp_test.sh
scp_test.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
行为不是这样的。
*编辑由于我使用 4.3(4.2.46) 下的 Bash 版本,我不能使用${dd@Q}
. 我通过使用引号组合'"${dd}"'
而不是 plain解决了这个问题${dd}
。
初步说明
这个答案主要是关于传统的
scp
,即scp
使用 SCP 协议的传统。OpenSSH 的Modernscp
默认使用 SFTP,当我写这篇文章时,过渡还不到两个月;所以很新鲜。我可以告诉你scp
使用 SCP,因为如果它使用 SFTP,你就不会遇到有问题的问题。解决此类问题是 SCP 被 SFTP 取代的原因之一。从变更日志:
潜在问题独立于
scp
首先:引用正确。即使未引用,您的示例名称也可能是安全的,仍然编写健壮的代码是一种美德。IMO 如果您可以不引用而逃脱,那么总是引用比每次都努力思考更容易。您的带有正确引用变量的代码将如下所示(注意我还没有尝试解决这个问题
scp
,这将在稍后完成):(我还将名称全部固定为大写,引入双破折号以防您添加以破折号开头的文件名
target
。我认为basename
在这种特殊情况下是无操作的,但我认为您有理由这样做。)如果您
scp
使用 SFTP,那么上面的代码就可以工作(坦率地说,我认为您的原始代码也可以使用这样scp
的 ,但只是因为您使用的名称在未引用时是“安全的”)。问题与
scp
不幸的是,遗留
scp
系统将远程路径名嵌入到 shell 代码中,该代码旨在由远程 shell 解释(比较我的这个答案)。远程 shell 将解释引号等字符$
,[
除非它们在到达远程 shell 时被转义或引用。这意味着您需要在本地额外引用远程 shell。您需要为本地 shell和远程 shell 引用。这就是引用的意思,即“要求文件名中的 shell 元字符双引号”。解决方案
巴什可以提供帮助。
"${dd@Q}"
将扩展dd
并转义或引用结果,因此在额外的解释级别(在您的情况下由远程外壳执行)之后,结果将是您期望的单个单词(如"$dd"
在本地扩展)。以下行是对 legacy 的修复scp
:整个脚本将是:
该解决方案不可移植,它不能在 pure 中工作
sh
。你的脚本中的 shebang 是#!/bin/bash
从一开始的,所以我猜你不介意只在 Bash 中工作的代码。最后的笔记
第一个片段(不带
${dd@Q}
)适用于scp
使用 SFTP(即新的)。第二个片段(带有${dd@Q}
)适用于scp
使用 SCP(即 legacyscp
)。没有简单的通用代码。使其表现得像 legacy的新scp
支持,但是如果您使用 legacy ,那么它将失败,因为它会发现该选项无效。无论哪种方式,您都需要知道您是新的还是旧的,并相应地调整您的 shell 代码。现在你的旧的(因此首先是问题),但如果你更新它,它可能会被一个新的取代。已经链接的变更日志注意到不兼容:-O
scp
scp
-O
scp
scp
一边“问题”,一个友好的建议。像这样命名你的脚本
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
.