OCaml - 致命错误:使用`|时出现异常Sys_error(“Broken pipe”)在包含许多行的输出上

时间:2014-03-03 02:12:31

标签: ocaml broken-pipe

我有很多行的文本文件。我想编写一个简单的OCaml程序,它将逐行处理这个文件并打印该行。

为了编写这个程序,我首先用较少的行创建了一个较小的文件 - 这样程序就可以更快地完成执行。

$ wc -l input/master 
214745 input/master
$ head -50 input/master > input/small-master

这是我写的简单样板filter.ml程序:

open Core.Std;;
open Printf;;
open Core.In_channel;;

if Array.length Sys.argv >= 2 then begin
  let rec process_lines ?ix master_file  =
    let ix = match ix with
      | None -> 0
      | Some x -> x
    in
    match input_line master_file with
    | Some line -> (
      if ix > 9 then printf "%d == %s\n" ix line;
      process_lines ~ix:(ix+1) master_file
    )
    | None -> close master_file
  in
  let master_file = create Sys.argv.(1) in
    process_lines master_file
end

它将输入文件的位置作为命令行参数,创建用于读取此文件的文件句柄,并使用此文件句柄作为参数调用递归函数process_lines

process_lines使用可选参数ix来计算从行逐句读取文件句柄时的行号。 process_lines只是打印从file_handle读取到标准输出的行。

然后,当我在较小的输入文件上执行程序并将输出传递给Linux head命令时,一切正常:

$ ./filter.native input/small-master |head -2
10 == 1000032|BINCH JAMES G|4|2012-11-13|edgar/data/1000032/0001181431-12-058269.txt
11 == 1000032|BINCH JAMES G|4|2012-12-03|edgar/data/1000032/0001181431-12-061825.txt

而且,当我在较大的文件上执行程序时,我看到了一个管道错误:

$ ./filter.native input/master |head -2
10 == 1000032|BINCH JAMES G|4|2012-11-13|edgar/data/1000032/0001181431-12-058269.txt
11 == 1000032|BINCH JAMES G|4|2012-12-03|edgar/data/1000032/0001181431-12-061825.txt
Fatal error: exception Sys_error("Broken pipe")
Raised by primitive operation at file "pervasives.ml", line 264, characters 2-40
Called from file "printf.ml", line 615, characters 15-25
Called from file "find.ml", line 13, characters 21-48
Called from file "find.ml", line 19, characters 2-27

我了解到管道的读者(在这种情况下是head命令)在管道的编写者(在本例中是我的OCaml程序)完成写入之前退出时会发生这样的管道错误。这就是为什么如果我使用tail命令作为读者,我永远不会得到这样的错误。

但是,当文件的行数较少时,为什么没有出现断管错误?

1 个答案:

答案 0 :(得分:2)

断管信号是Unix设计的基本部分。当您拥有a | b b仅读取少量数据的管道时,a已阅读其所需内容后,您不希望b浪费时间撰写。为了实现这一点,Unix将损坏的管道信号发送到一个进程,该进程写入一个没人正在读取的管道。在通常情况下,这会导致程序以静默方式退出(即,它会杀死程序),这正是您想要的。

在这个假设的例子中,b在读完几行后退出,这意味着没有人正在读取管道。下一次a尝试写入更多输出时,会发送断开的管道信号并退出。

在您的情况下,a是您的计划,bhead

似乎OCaml运行时正在注意信号并且没有静默退出。您可以认为这是一个缺陷,或者可能知道信号何时终止您的程序。修复它的最好方法是自己捕获信号并静默退出。

小文件没有发生的原因是整个输出适合管道。 (管道表示大约64K字节的缓冲区。)您的程序只是写入其数据并退出;你的程序没有足够的时间尝试写入没有读者的管道。