Bash Systemd ExecStart 中的大括号扩展

Bash Brace Expansion in Systemd ExecStart

以下测试在CentOS 7.1中进行

/usr/lib/systemd/system/

中创建以下文件 test.service
[Unit]
Description=Test Bash Brace Expansion
Wants=network.target
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c "a='hello world'; echo ${a}"

[Install]
WantedBy=multi-user.target

并执行systemctl daemon-reload; systemctl restart test; systemctl status test -l

没有值的输出,因为 ${a} 没有替换为单词 hello world,直到它被 echo ${a} 更改为

  1. echo $a:工作
  2. echo $${a}:工作

$$ 表示 bash 中进程的 pid,但为什么 $$ 可以在 ExecStart 中使用这个技巧来获得单词 hello world?

大括号与参数扩展

你做的不是大括号展开,而是参数展开。大括号扩展(例如 ${1..5} 在双引号内不起作用。

你的问题用两个 Bash shells

解释

参数扩展 在双引号内工作,但如果替换应该发生在被调用的 shell 而不是当前的 $符号需要被引用。

考虑这个例子:

a='bye world' ; /bin/bash -c "a='hello world'; echo ${a}"
bye world

对比这个:

a='bye world' ; /bin/bash -c "a='hello world'; echo ${a}"
hello world

将解决方案应用于 systemd 服务文件以及 `$$` 起作用的原因

重要的是要记住,您的 systemd 服务描述文件不会在 Bash 中执行(如上面的示例),因此有一些非常相似但略有不同的规则,请参阅 documentation for service unit configuration files.

您的问题是,就像上面第一个示例中的交互式 Bash 一样,systemd 正在尝试扩展其环境中没有的 ${a}。正如您已经注意到的,$$ 是您的解决方案,上面 link 解释了原因:

To pass a literal dollar sign, use "$$".

这允许在 Bash shell systemd 调用时进行参数扩展。所以配置文件中的行应该是:

ExecStart=/bin/bash -c "a='hello world'; echo $${a}"