Systemd字符串转义

11

如果我运行这个命令

/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} 的返回结果。

有任何想法,为什么文件内部的转义不同?我该如何解决?谢谢!

3个回答

14
systemd-escape '\"a fun thing"\' 

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

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

将会打印出一件有趣的事情


1
OP 的问题是如何从 echo 中发出字面引号。也就是说,他们想知道如何打印 "a fun thing" 而不是 a fun thing - Charles Duffy

7

Systemd的操作方式与bash不同,因此会出现转义问题。实际上,在解析后,systemd会删除单引号和双引号。这个事实来自于文档(我也经历过这个问题,然后阅读了文档:D)。

解决方法是,如果你的目的允许,可以调用一个脚本来回显所需的信息(带有转义引号)。


1
我不明白,请看一下这个单元文件 https://gist.github.com/digital-wonderland/e0bd8e0d4c91a7fec2c7#file-elasticsearch-service ... 它充满了双引号,而且如果没有它们,它甚至无法工作,对吧? - lisak
3
这是不正确的说法。我的意思是,是的,systemd确实会去掉引号,但它这样做的方式与shell去掉引号的方式相同(是的,正常的shell解析确实有一个称为“引号移除”的步骤),因为它在使用引号来指导字符串拆分后删除文字引号。ExecStart=/usr/bin/docker run something/else -- -c "a fun thing"ExecStart=/usr/bin/docker run something/else -- -c a fun thing 的行为绝对不相同 - Charles Duffy
是的,在日志中它显示了argv元素之间实际分隔符不正确的方式,但如果您查看被调用服务的/proc/$pid/cmdline,您将看到边界处正确的NULs。 - Charles Duffy
我甚至不确定Naftuli Kay的编辑是何时发生的,但我不认为我添加了那部分内容。@CharlesDuffy - MiiinimalLogic
哦,。@NaftuliKay的那个编辑真的应该是一个单独的答案。 - Charles Duffy
@Naftuli Kay,你在我的答案中添加了什么? - MiiinimalLogic

6
简而言之,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 命令的输出中,他们就可能通过在 port 变量中加入 , "evil": true 来插入其他键值对到您的数据中)。使用 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传递任何反斜杠到shell。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接