我想在命名管道上运行tail以便于进行一些二进制日志文件处理。问题是神秘数据被添加到流的开头。我通过使用打开的端口(open_port)启动erlang进程来运行我的测试,然后我使用另一个shell将bin捕获到命名管道中。
这是一个从端口获取数据的简单函数:
bin_from_tail() ->
open_port({spawn,"/usr/bin/tail -F named_pipe"},
[binary,in,eof]),
receive
{_,{data,<<Data/binary>>}} -> Data
end.
所以我有两种方法可以获取相同的数据......
创建命名管道
mkfifo named_pipe
此命令将阻止,直到您从另一个shell运行“cat log.bin&gt; named_pipe”
{ok,TailBin} = file:read_file(log.bin)。
使用erlang文件库将整个文件读入内存 FileBin = file:read_file(log.in)。
但是TailBin和FileBin不一样! TailBin在开头有一个神秘的120字节字符串:
<<40,6,161,69,172,216,56,14,100,0,80,6,0,0,0>>
答案 0 :(得分:2)
感谢关于无休止地循环cat /重启死端口的想法。似乎命名管道缓冲了一点点,所以如果端口打开得足够快,则编写器进程(另一个程序)不会崩溃!绝对有风险的东西,但就黑客来说......它有效。
因为所说的所有邮件列表帖子都是这样做的,所以没有例子就这样做,我将发布我的工作方式!如果有人想提供改进,请随时这样做。我的解决方案:
read() ->
Port = open_port({spawn,"/bin/cat /path/to/pipe"},
[binary,in,eof]),
do_read(Port).
do_read(Port) ->
receive
{Port,{data,<<Data/binary>>}} ->
case do_something:with(Data) of
ok ->
io:format("G") % Good
Any ->
io:format("B") % Bad
end;
{Port,eof} ->
read();
Any ->
io:format("No match fifo_client:do_read/1, ~p~n",[Any])
end,
do_read(Port).
答案 1 :(得分:1)
我发现在erlang之外发生了同样的事情。问题是 tail 试图向您显示文件的 end ,而不是整个文件。如果你在普通文件上使用它,任何写的都是新的,并由 -f 选取,但在这种情况下看起来像 tail 正在等待直到结束文件(通过管道传来的eof)然后显示最后10行(将二进制文件视为文本)。
tail -F -c 9999999
(假设你的日志是9999999字节或更少)可能会有效。
也许尝试使用 cat 而不是 tail -F ,这似乎对我有用。然后你只需要避免 cat 退出eof的事实,我认为你试图通过使用tail来避免。
所以一个shell脚本无休止地循环 cat ,也许?
或者让erlang重新启动关闭并在它死亡时重新创建端口,因为你无论如何都要获得eof信号。或者在进程退出时使用exit_status标志open_port进行信号通知,因此需要区分eof和进程退出。 (如果同时使用exit_status和eof,eof永远不会出现,使用 cat&lt; / dev / null 进行简短测试表明)