我正在编写一个应该启动 LTE 连接的服务。
但是我用来连接的实用程序(sakis3g)正在写入/dev/stderr
,这在 systemd 中不可用,并且日志充满了
Cannot create /dev/stderr: No such device or address
有什么方法可以解决这个问题吗?
不幸的是,更改实用程序脚本/二进制文件不是一个选项。
编辑:这是服务:
[Unit]
Description=LTE
After=syslog.target network-online.target
Before=openvpn-client@client
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/mav/LTE/
ExecStartPre=/sbin/ip link set wwan0 down
ExecStart=/opt/mav/LTE/sakis3g connect -g --sudo
ExecStop=/opt/mav/LTE/sakis3g disconnect --sudo
[Install]
WantedBy=multi-user.target
并且它在这种状态下工作。但是,当我从更详细的调试输出标志开始时sakis3g
,-g
就会stderr
出现错误。
答案1
如果没有 stderr,您将不会看到该错误。
在这里,错误是关于无法打开该/dev/stderr
文件,在 Linux 上是指向 fd 2 上打开的实际文件的符号链接(而在其他系统上,打开 /dev/stderr 就像执行 a 一样dup(2)
)。
这里的问题可能是 fd 2 在套接字(inet TCP、Unix 流或其他)上打开,而您不能打开open()
套接字文件。
如果你运行:
sudo lsof -aU +E -d 2
在使用的系统上,systemd
您会注意到大多数服务的 fd 2 是到systemd-journald
.
作为解决方法,您可以将其启动为:
bash -o pipefail -c '{ /opt/mav/LTE/sakis3g connect -g --sudo 2>&1 >&3 3>&- | cat >&2 3>&-; } 3>&1'
也就是说,使 stderr 成为管道(to cat
)而不是套接字,cat
将管道上接收到的内容转发到原始 stderr (套接字),并确保我们使用该pipefail
选项保留命令的退出状态。
无论如何,其来源sakis3g
是可用的并且sakis3g
初始化文件是一个 bash 脚本,它恰好执行以下操作:
echo text >> /dev/stderr
代替:
echo test >&2
甚至还有一些> /dev/stderr
反而>> /dev/stderr
更错误的。它没有tee -a /dev/stderr | other cmd
合法使用,/dev/stderr
也不能与套接字上的 stderr 一起使用,因此很容易修复。
您可能希望通过在以下地址提出问题来让他们知道该问题:https://github.com/Trixarian/sakis3g-source/issues
答案2
tee
我在尝试在服务内进行标准输出时遇到了类似的问题。虽然这不是OP的确切问题是另一个答案称其为“合理使用/dev/stderr
”,所以我觉得值得在这里发布,以防未来有人需要它。
也就是说我有以下内容:
command1 | tee /proc/$$/fd/1 | command2
在这种情况下,/proc/$$/fd/1
始终是 shell 的/dev/stdout
,因此它通常在管道中工作。尽管拒绝打开套接字,但它仍然存在相同的问题。
我想出的解决方案是tee
通过进程替换来交换:
command1 | tee >(command2)
这还没有完全实现,因为现在 Bash 不会等待command2
退出。为了解决这个问题,我们可以使用以下方法这个另一个答案:
{ { command1 | tee >(command2); } 3>&1 >&4 4>&- | cat; } 4>&1