如何解决“tail -f”不发出 EOF 的问题并使其与“csvtool”等工具一起使用?

如何解决“tail -f”不发出 EOF 的问题并使其与“csvtool”等工具一起使用?

我基本上想这样做:

tail -f trades.csv | csvtool readable -

我想使用 csvtool 以可读格式读取 CSV 文件,并且想继续观看它。

我认为该命令不起作用,因为tail -f从不发出流结束信号,因此 csvtool 无限期地等待。当然,这个常见问题有解决方法吗?

谢谢

答案1

不存在“发出 EOF”这样的事情。 EOF 不是带外信号。 EOF 是指尝试读取时报告没有剩余数据可供读取。

如果将 的输出通过管道传输tail -f到一个在开始发出输出之前读取整个输入的程序,则该程序在读取完整输入之前不会发出任何输出。由于tail -f永远不会关闭其输出(因为它永远不会停止发出输出),因此只有在终止尾部进程后才会发生这种情况。

csvtool readable读取所有输入行,然后确定每个单元格的宽度,计算每列中单元格的最大宽度,最后发出宽度一致的所有行和列。在所有输入都可用之前不可能执行此计算,因为最后一行可能是具有最宽单元格的一行。因此,在逻辑上不可能csvtool readable以在读取所有输入之前开始发出输出的方式进行设计。

也许您不关心所有行具有相同的列宽。也许您只想要大部分宽度,如果出现更宽的行,宽度就会放大。这是合理的。但这不是 csvtool 提供的功能。

在许多情况下,“逐渐发出输出foo | bar时不会立即发出输出foo”是由于foo.看关闭管道中的缓冲。但这不是这里发生的事情。对于不需要整个输入的 csvtool 子命令,输入来自缓冲其输出的程序,这可能是不同情况下的问题。

如果您只想将 CSV 中的逗号转换为某种列对齐方式,并且您愿意手动指定列宽,那么这里有一个两行:

tail -f … | python3 -u -c 'import csv, sys 
for row in csv.reader(sys.stdin): print("\t".join(row))' | expand -t 11,13,17

expand如果您对大多数终端和编辑器使用的每 8 列默认制表符感到满意,则不需要此步骤。

1对于挑剔者:超出第一行的第一个单元格,这没有帮助。

答案2

使用(以前称为 Perl_6)

Raku 实现了“CAP”编程架构:并发、异步和并行。 “CAP”编程的许多方面对于流数据都是有利的。 Raku 的JSON::Stream包可以处理流式 JSON 数据。然而,目前还不清楚CSV用 Raku 编写的真正的解析器是否可以利用这种架构。

如果您只想用逗号分割行(行),则以下代码有效。它实现了一个react/wheneverRaku 中的块(“CAP”架构)。下面不会处理嵌入的换行符、双引号内嵌入的逗号,但它是一个开始(也在 上进行了测试/var/log/system.log):

~$ tail -n2 -f MS.csv | raku -e 'react {  \
                                 whenever Supply( $*IN.lines ) -> $ln {  \
                                 .split(",").raku.match(/^^ <-["]>+  <( \" .+ \" )>  <-["]>+ $$/).put for $ln } };'

输入样本(来自https://www.microsoft.com/en-us/download/details.aspx?id=45485):

User Name,First Name,Last Name,Display Name,Job Title,Department,Office Number,Office Phone,Mobile Phone,Fax,Address,City,State or Province,ZIP or Postal Code,Country or Region
[email protected],Chris,Green,Chris Green,IT Manager,Information Technology,123451,123-555-1211,123-555-6641,123-555-9821,1 Microsoft way,Redmond,Wa,98052,United States
[email protected],Ben,Andrews,Ben Andrews,IT Manager,Information Technology,123452,123-555-1212,123-555-6642,123-555-9822,1 Microsoft way,Redmond,Wa,98052,United States
[email protected],David,Longmuir,David Longmuir,IT Manager,Information Technology,123453,123-555-1213,123-555-6643,123-555-9823,1 Microsoft way,Redmond,Wa,98052,United States
[email protected],Cynthia,Carey,Cynthia Carey,IT Manager,Information Technology,123454,123-555-1214,123-555-6644,123-555-9824,1 Microsoft way,Redmond,Wa,98052,United States
[email protected],Melissa,MacBeth,Melissa MacBeth,IT Manager,Information Technology,123455,123-555-1215,123-555-6645,123-555-9825,1 Microsoft way,Redmond,Wa,98052,United States

示例输出(仅处理最后 2 行,通过tail -n2 -f):

"cynthia\@contoso.com", "Cynthia", "Carey", "Cynthia Carey", "IT Manager", "Information Technology", "123454", "123-555-1214", "123-555-6644", "123-555-9824", "1 Microsoft way", "Redmond", "Wa", "98052", "United States"
"melissa\@contoso.com", "Melissa", "MacBeth", "Melissa MacBeth", "IT Manager", "Information Technology", "123455", "123-555-1215", "123-555-6645", "123-555-9825", "1 Microsoft way", "Redmond", "Wa", "98052", "United States"

上面,要接收未加引号的输出,只需使用.put,删除对以下内容的中间调用:

.raku.match(/^^ <-["]>+  <( \" .+ \" )>  <-["]>+ $$/)

注意:我尝试过 Raku 的Text::CSV模块,看看它是否可以与 Raku 的模块一起使用react/whenever块,但到目前为止还没有运气。我能做的最好的事情就是实现一个while块,如果您只是为其提供tail输入,那么这是一个不错的解决方案。代码如下:

~$ tail -n2 -f MS.csv | raku -MText::CSV -e 'my @rows;  \
                                             my $csv = Text::CSV.new;  \
                                             while ($csv.getline($*IN)) -> $row {  \
                                             @rows.push: $row; say @rows[*-1].raku; };'

示例输出:

$["cynthia\@contoso.com", "Cynthia", "Carey", "Cynthia Carey", "IT Manager", "Information Technology", "123454", "123-555-1214", "123-555-6644", "123-555-9824", "1 Microsoft way", "Redmond", "Wa", "98052", "United States"]
$["melissa\@contoso.com", "Melissa", "MacBeth", "Melissa MacBeth", "IT Manager", "Information Technology", "123455", "123-555-1215", "123-555-6645", "123-555-9825", "1 Microsoft way", "Redmond", "Wa", "98052", "United States"]

[在上面,删除对获取不带引号的输出的调用.raku,或附加对保留双引号的调用.match(/^^ <-["]>+ <( \" .+ \" )> <-["]>+ $$/),删除行首/结尾处的无关字符]。

上面的代码将传入的数据推送到@rows数组中,以防您想对其执行某些操作。最重要的是,因为Text::CSV它是一个真正的 CSV 解析器,所以您可以验证 CSV 输入。并且由于输入是经过验证的 CSV,因此您可以直接输出列,或每行元素数等。例如,将最后一条语句替换say @rows[*-1]say @rows[*-1][2]以接收第三列的连续输出。

请参阅下面的 URL 了解sep-charescape-charformula-handlingbinarystrict设置等。

https://raku.land/github:Tux/Text::CSV
https://github.com/Tux/CSV
https://raku.org

答案3

如果csvtool 需要EOF——你运气不好。

但如果问题出在管道缓冲上,那么以下两者之一可能会有所帮助:

$ unbuffer tail -f trades.csv | csvtool readable -

$ stdbuf -i0 -o0 -e0 tail -f trades.csv | csvtool readable -

相关内容