【问题标题】:Bash - Escaping SSH commandsBash - 转义 SSH 命令
【发布时间】:2013-05-29 01:25:24
【问题描述】:

我有一组脚本,用于通过 FTP 下载文件,然后从服务器中删除它们。

它的工作原理如下:

for dir in `ls /volume1/auto_downloads/sync-complete`
do
if [ "x$dir" != *"x"* ]
then
echo "DIR: $dir"

echo "Moving out of complete"
        # Soft delete from server so they don't get downloaded again
        ssh dan@172.19.1.15 mv -v "'/home/dan/Downloads/complete/$dir'" /home/dan/Downloads/downloaded

现在 $dir 可以是“这是一个文件”,它可以正常工作。

我遇到的问题是特殊字符,例如:

  • “这是(一个)文件”
  • 这是一个文件和东西"

容易出错:

bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `mv -v '/home/dan/Downloads/complete/This is (a) file' /home/dan/Downloads/downloaded'

我不知道如何对其进行转义,以便对变量进行评估并正确转义命令。我尝试了转义字符、文字引号、普通引号等的各种组合

【问题讨论】:

  • rsync --remove-source-files serverfault.com/a/363925/69736 怎么样。容易多了!
  • 目前尚不清楚dir 是如何设置为单个字符串 'This is (a) file. It should be set successively to 4 separate strings 'This', 'is', '(a)', and 'file'. Don't parse the output of ls`;切换目录并遍历一个 glob(有关详细信息,请参阅我的答案)。
  • [ "x$dir" != *"x"* ] -- 这仅适用于[[ 中的模式匹配,不适用于[

标签: bash ssh


【解决方案1】:

可以用pythonshlex.quote(s)

返回字符串 s 的 shell 转义版本

docs

【讨论】:

  • 这比 printf %q 方法有一个优势,因为它返回的字符串可以保证在所有 POSIX 兼容的 shell 上工作,而不仅仅是 bash。
【解决方案2】:

如果双方都使用bash,可以使用printf '%q '转义参数,例如:

ssh dan@172.19.1.15 "$(printf '%q ' mv -v "/home/dan/Downloads/complete/$dir" /home/dan/Downloads/downloaded)"

【讨论】:

  • printf只在本端执行,所以远程主机上不需要bash
  • %q 根据 Bash 的转义规则进行转义。如果 bash 不是远程 shell,则无法保证转义符有效。
  • ...也可以考虑使用printf -v cmd_q '%q ' ...,然后使用ssh user@host "$cmd_q",完全避免支付subshel​​l惩罚。
  • 顺便说一句,在现代 bash(当然是 5.x,也许是 4.x 后期)中,可以ssh dan@host "mv -v /path/to/${dir@Q} /path/to/Downloads
【解决方案3】:

Will Palmer 关于使用printf 的建议很棒,但我认为将文字部分放在printf 的格式中更有意义。

这样,多命令单行代码写起来更直观:

ssh user@host "$(printf 'mkdir -p -- %q && cd -- "$_" && tar -zx' "$DIR")"

【讨论】:

    【解决方案4】:

    我很困惑,因为您编写的代码对我有用:

    > dir='foo & bar (and) baz'
    > ssh host  mv -v "'/home/dan/Downloads/complete/$dir'" /home/dan/Downloads/downloaded
    mv: cannot stat `/home/dan/Downloads/complete/foo & bar (and) baz': No such file or directory
    

    对于调试,使用脚本顶部的set -vx 来查看发生了什么。

    【讨论】:

    • 试试dir="nathan's crew.txt"
    • @Will,你让我上了那个,所以你的答案肯定比我的好,但我仍然对为什么 OP 对他发布的脚本有问题感到困惑。
    • 实际问题来自通过 SSH 传递远程命令的方式:所有参数都连接在一起并作为单个字符串传递到远程 shell - 所以原始帖子示例中的那些双引号实际上只是为了使单引号存在于远程端。
    • @Will,是的,但除非$dir 包含单引号(或可能是感叹号),否则它应该可以工作。 OP 说它被括号绊倒了。
    【解决方案5】:

    你需要引用整个表达式ssh user@host "command":

    ssh dan@172.19.1.15 "mv -v /home/dan/Downloads/complete/$dir /home/dan/Downloads/downloaded"
    

    【讨论】:

    • 这导致在远程端而不是本地端评估变量
    • 哦,我明白了。所以我在没有$ 转义的情况下更新了我的答案。它现在应该可以工作了。
    • 啊现在我看到你在ssh 命令之前有一个echo。所以更好的做法是将其包装在$() 中,正如@Will Palmer 的回答所示。
    猜你喜欢
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 2013-12-28
    • 2018-10-19
    • 2013-03-12
    • 1970-01-01
    • 1970-01-01
    • 2013-10-03
    相关资源
    最近更新 更多