将命名管道输入重定向到文件
Posted
技术标签:
【中文标题】将命名管道输入重定向到文件【英文标题】:Redirect named pipe input to file 【发布时间】:2019-10-30 12:27:02 【问题描述】:我想创建一个可以写入的文件,如Datadog Datagram docs 中所述:
echo -n 'a' >/dev/udp/localhost/8125
echo -n 'b' >/dev/udp/localhost/8125
echo -n 'c' >/dev/udp/localhost/8125
写入该文件的所有内容都应该(而不是由 Datadog 处理并通过代理发送给他们)写入日志文件。执行上述三行后,日志文件应包含以下内容:
a
b
c
我认为一个命名管道和一个处理它的后台进程将是完美的。然而,它并没有按预期工作,后台进程从不写任何东西,即使写似乎工作。
我创建了以下脚本:
#!/usr/bin/env bash
set -Eeuo pipefail
log=/var/log/datadog-agent.log
touch $log
# https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/
pipe=/dev/udp/localhost/8125
if [[ ! -p $pipe ]]; then
rm -f $pipe
mkdir -p "$(dirname $pipe)"
mkfifo -m 0666 $pipe
fi
trap 'rm -f $pipe' EXIT
while :; do
read -r line <$pipe
echo "$line" >>$log
done
还有以下 systemd 服务:
[Unit]
Description=Fake Datadog Agent
[Service]
ExecStart=/usr/local/bin/datadog-agent
Type=exec
[Install]
WantedBy=multi-user.target
服务在执行systemctl enable --now datadog-agent
后正确启动,但是,正如我所说,没有任何内容被写入日志文件。
这对我来说很奇怪,因为打开了两个 shell 实例,我在第一个 shell 中编写了以下内容:
mkfifo pipe
while :; do read -r line <pipe; echo "$line"; done
然后在第二个 shell 中开始发送数据正确打印行。
【问题讨论】:
mkfifo -m 0666 /dev/udp/localhost/8125
- 你认为这里会发生什么? /dev/udp/*
不是文件,不是fifo,它是经过特殊处理的特殊文件。如果您在/dev/udp
创建自己的fifo,则不再处理它,您只是在写入fifo。
@KamilCuk 我认为内核不知道 /dev/udp 和 /dev/tcp。我相信这些是只有 shell 才能识别的特殊字符串。 bash 处理 /dev/udp 和 /dev/tcp 并尝试创建套接字。
这个 datadog 程序从 udp 套接字 读取,而不是管道或文件。
我想将回显到该特定路径的任何内容捕获到插入换行符的文件中。
查看 bash 源代码,我相信您将不得不 patch bash to do that。无论如何,看起来都像 bash redirects 路径。在不改变写作方面的情况下,我认为您无能为力,echo 123 | tee /dev/udp/localhost/8125
之类的东西可能会起作用。
【参考方案1】:
问题的答案在它的 cmets 中找到。因此,这个问题不应该没有答案。
问题中的代码按预期工作,但是,命名管道所在的路径是一个特殊路径,这就是发送给它的数据永远不会到达脚本的原因。 Bash 中对应的特殊大小写可以在redir.c
中找到。
解决问题的方法是在该端口上使用真正的 UDP 服务器:
socat -u -v -x udp-listen:8125,fork /dev/null &>/var/log/datadog-agent.log
【讨论】:
以上是关于将命名管道输入重定向到文件的主要内容,如果未能解决你的问题,请参考以下文章
将子进程的 stdout 和 stderr 重定向到两个命名管道(然后从它们读回)