【问题标题】:Systemd string escaping系统字符串转义
【发布时间】:2014-07-19 02:15:57
【问题描述】:

如果我运行这个命令

/bin/bash -c 'while true;do /usr/bin/etcdctl set my-container "{\"host\": \"1\", \"port\": $(/usr/bin/docker port my-container 5000 | cut -d":" -f2)}" --ttl 60;sleep 45;done'

我从 etcd 得到了我期望的结果 {"host":"1", "port":49155}

但是如果我把它放在一个 systemd 文件中

ExecStart=/bin/bash -c 'while true;do /usr/bin/etcdctl set my-container "{\"host\": \"1\", \"port\": $(/usr/bin/docker port my-container 5000 | cut -d":" -f2)}" --ttl 60;sleep 45;done'

我回来了 {host:1, port:49155}

知道为什么文件内部的转义不同吗?我该如何解决?谢谢!!

【问题讨论】:

    标签: systemd etcd


    【解决方案1】:
    systemd-escape '\"a fun thing"\' 
    

    输出:\x5c\x22a\x20fun\x20thing\x22\x5c

    [Service]
    ExecStart=/bin/sh -c 'echo "\x5c\x22a\x20fun\x20thing\x22\x5c"'
    

    将打印a fun thing

    【讨论】:

    • OP 的问题是能够从echo 发出文字引号。也就是说,他们想知道如何打印"a fun thing",而不是a fun thing
    【解决方案2】:

    Systemd 所做的不像您现在所知道的 bash,因此存在转义问题。实际上,systemd 在解析单引号和双引号后会删除它们。这个事实就在文档之外(我也通过了这个,然后阅读:D)。

    解决方案,如果您的目的允许,请调用一个脚本来回显您需要的信息(使用转义引号)。

    【讨论】:

    • 没看懂,请看一下这个单元文件gist.github.com/digital-wonderland/…... 满满的都是双引号,没有双引号根本不行吧?
    • 这是不真实的。我的意思是,是的,systemd does 删除引号,但它这样做的意义与 shell 删除引号相同(是的,正常的 shell 解析 does 有一个称为“引号删除”),因为它会在使用它们来指导字符串拆分之后删除文字引号。 ExecStart=/usr/bin/docker run something/else -- -c "a fun thing" 在行为上与ExecStart=/usr/bin/docker run something/else -- -c a fun thing绝对不相同
    • (是的,它在日志中显示它的方式并不代表 argv 元素之间的实际划分,但是如果您查看 /proc/$pid/cmdline 的调用服务,您将看到边界正确所属的 NUL)。
    • 我什至不确定 naftuli Kay 的编辑是什么时候发生的,但我认为我没有添加那部分 @CharlesDuffy
    • 哦,。 @NaftuliKay 的编辑真的应该是一个单独的答案。
    【解决方案3】:

    简而言之——这是不同的,因为 systemd 自己进行字符串拆分、转义和扩展,而且它使用的逻辑不符合 POSIX。

    你仍然可以做你想做的事,但你需要更多的反斜杠:

    ExecStart=/bin/bash -c '\
      while :; do \
        port=$(/usr/bin/docker port my-container 5000 | cut -d: -f2); \
        /usr/bin/etcdctl set my-container "{\\\"host\\\": \\\"1\\\", \\\"port\\\": $port}" --ttl 60; \
        sleep 45; \
      done'
    

    注意在所需输出中对每个文字 " 字符使用 \\\"


    顺便说一句——就我个人而言,我建议不要尝试通过字符串连接来生成 JSON——它很容易出现注入漏洞(如果有人可以将他们选择的内容放在 docker port 命令的输出中,他们可能会插入通过将, "evil": true 放在port 变量中,将其他键/值对添加到您的数据中)。使用jq可以避免此类问题:

    ExecStart=/bin/bash -c '\
      while :; do \
        port=$(/usr/bin/docker port my-container 5000 | cut -d: -f2); \
        json=$(jq -nc \
          --arg host 1 \
          --arg port "$port" \
          '{} | .host=$host | .port=($port | tonumber)'); \
        /usr/bin/etcdctl set my-container "$json" --ttl 60; \
        sleep 45; \
      done'
    

    作为一个愉快的副作用,上面避免了需要任何文字双引号字符(唯一使用的是sh副本的语法),所以我们不需要从systemd传递任何反斜杠到外壳。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-09
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多