赞
踩
今天一个同事告诉我遇到了一个问题:有一系列的sh和golang写的脚本,相互之间是通过stdout传递结果,在手工调用的时候工作良好,但是在托管给systemd的时候却不工作。怀疑是systemd截取了stdout,导致程序失败。
这个怀疑不无道理,因为在systemd的配置文件中有个很可以的配置项: StandardOutput=journey, 顺便贴出来官网对这个字段的解释:
StandardOutput=
Controls where file descriptor 1 (STDOUT) of the executed processes is connected to. Takes one of inherit
, null
, tty
, journal
, syslog
, kmsg
, journal+console
, syslog+console
, kmsg+console
, file:
, path
socket
or fd:
.name
inherit
duplicates the file descriptor of standard input for standard output.
null
connects standard output to /dev/null
, i.e. everything written to it will be lost.
tty
connects standard output to a tty (as configured via TTYPath=
, see below). If the TTY is used for output only, the executed process will not become the controlling process of the terminal, and will not fail or wait for other processes to release the terminal.
journal
connects standard output with the journal which is accessible via journalctl(1). Note that everything that is written to syslog or kmsg (see below) is implicitly stored in the journal as well, the specific two options listed below are hence supersets of this one.
syslog
connects standard output to the syslog(3) system syslog service, in addition to the journal. Note that the journal daemon is usually configured to forward everything it receives to syslog anyway, in which case this option is no different from journal
.
kmsg
connects standard output with the kernel log buffer which is accessible via dmesg(1), in addition to the journal. The journal daemon might be configured to send all logs to kmsg anyway, in which case this option is no different from journal
.
journal+console
, syslog+console
and kmsg+console
work in a similar way as the three options above but copy the output to the system console as well.
The file:
option may be used to connect a specific file system object to standard output. The semantics are similar to the same option of path
StandardInputText=
, see above. If standard input and output are directed to the same file path, it is opened only once, for reading as well as writing and duplicated. This is particular useful when the specified path refers to an AF_UNIX
socket in the file system, as in that case only a single stream connection is created for both input and output.
socket
connects standard output to a socket acquired via socket activation. The semantics are similar to the same option of StandardInput=
, see above.
The fd:
option connects standard output to a specific, named file descriptor provided by a socket unit. A name may be specified as part of this option, following a "name
:
" character (e.g. "fd:foobar
"). If no name is specified, the name "stdout
" is implied (i.e. "fd
" is equivalent to "fd:stdout
"). At least one socket unit defining the specified name must be provided via the Sockets=
option, and the file descriptor name may differ from the name of its containing socket unit. If multiple matches are found, the first one will be used. See FileDescriptorName=
in systemd.socket(5) for more details about named descriptors and their ordering.
If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the kernel log buffer, the unit will implicitly gain a dependency of type After=
on systemd-journald.socket
(also see the "Implicit Dependencies" section above). Also note that in this case stdout (or stderr, see below) will be an AF_UNIX
stream socket, and not a pipe or FIFO that can be re-opened. This means when executing shell scripts the construct echo "hello" > /dev/stderr for writing text to stderr will not work. To mitigate this use the construct echo "hello" >&2 instead, which is mostly equivalent and avoids this pitfall.
This setting defaults to the value set with DefaultStandardOutput=
in systemd-system.conf(5), which defaults to journal
. Note that setting this parameter might result in additional dependencies to be added to the unit (see above).
准备两个脚本 a.sh 和 b.sh
a.sh:
#!/bin/sh
b=`./b.sh`
echo $b
echo "in a.sh foo foo foo" >> /root/test/log.bak
echo $b >> /root/test/log.bak
b.sh
#!/bin/sh
echo "in b.sh xxxxxxxxxxx"
OK, 代码很简单,运行结果如下:
[root@myhost test]#./a.sh
b.sh xxxxxxxxxxx
[root@myhost test]# cat log.bak
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
结果看起来确实不错,再试试托管给systemd, 配置文件test-for-me.service如下:
[Unit]
Description=Test
[Service]
WorkingDirectory=/
User=root
Group=root
ExecStart=/root/test/a.sh
Restart=always
StandardOutput=journal
StandardError=inherit
Nice=19
LimitNOFILE=16384
# Disable timeout logic and wait until process is stopped
TimeoutStopSec=25
# SIGTERM signal is used to stop the Java process
KillSignal=SIGTERM
# Send the signal only to the JVM rather than its control group
KillMode=process
# When a JVM receives a SIGTERM signal it exits with code 143
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
Enable这service:
systemctl enable test-for-me
启动这个service:
systemctl start test-for-me
在看看log.bak
[root@suitetest test]# cat log.bak
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
果然不妙啊,“in b.sh xxxxxx”哪里去了,真的被systemd截取了?
赶紧查查systemd的log:
发现了猫腻:
Jan 15 10:35:31 suitetest a.sh[32038]: /root/test/a.sh: line 3: ./b.sh: No such file or directory
找不到b.sh,怎么会,查看a.sh,不就是在同一路径下的调用吗:
b=`./b.sh`
嗯,老司机在这里估计会心里一动,虎躯一震,难道是路径的问题?
改!
b=`/root/test/b.sh`
呵呵,结果如您所愿:
[root@suitetest test]# cat log.bak
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
in a.sh foo foo foo
in b.sh xxxxxxxxxxx
以为是什么难解的问题,最后是个路径问题,所以呢,如果遇到问题在google查不到,那么先怀疑下解决问题的方向。
随手记下,以供后查。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。