bash zcat头导致pipefail?

时间:2017-01-06 23:45:00

标签: bash

['a', 'b', 'c'].each do |el|
          table_for ratings.list do
            column el do |list|
              table_for rating.list[el] do
                column :rating
                column :comment
              end
            end
          end
      end

工作正常

set -eu 
VAR=$(zcat file.gz  |  head -n 12)

导致bash退出失败。 这怎么会导致管道故障?

请注意,file.gz包含数百万行(约750 MB,已压缩)。

2 个答案:

答案 0 :(得分:16)

考虑一下。

  1. 您告诉shell,如果任何组件发生故障,应该认为您的整个管道都已失败。
  2. 您告诉zcat将其输出写入head
  3. 然后你告诉head在读取12行之后退出,而不是超过12行的输入流。
  4. 当然你有一个错误:zcat早期关闭了目标管道,并且无法成功编写输入文件的解压缩版本!它没有任何方式知道这是由于用户意图,通过错误发生的事情。

    如果您使用zcat写入磁盘并且空间不足或网络流耗尽并且连接丢失,那么以状态退出是完全正确的表示失败。这只是该规则的另一种情况。

    操作系统给出zcat的具体错误是EPIPE,由write系统调用在以下条件下返回:尝试写入任何进程都无法读取的管道。

    head(此FIFO的唯一读取器)退出后,对于任何写入管道输入端而不是以返回EPIPE将是一个错误。对于zcat静默忽略写入其输出的错误,从而能够生成不准确的输出流而没有反映此事件的退出状态,同样将成为错误。

    如果您不想更改任何shell选项,顺便提一下,您可能会考虑使用进程替换的一种解决方法:

    var=$(head -n 12 < <(zcat file.gz))
    

    在这种情况下,zcat不是管道组件,并且不会考虑其退出状态以确定成功。 (如果您想要进行独立的成功/失败判断,您可以测试$var是否长12行。

    通过使用本机gzip支持引入Python解释器,可以实现更全面的解决方案。嵌入在shell脚本中的本机Python实现(兼容Python 2和3.x)可能如下所示:

    zhead_py=$(cat <<'EOF'
    import sys, gzip
    gzf = gzip.GzipFile(sys.argv[1], 'rb')
    outFile = sys.stdout.buffer if hasattr(sys.stdout, 'buffer') else sys.stdout
    numLines = 0
    maxLines = int(sys.argv[2])
    for line in gzf:
        if numLines >= maxLines:
            sys.exit(0)
        outFile.write(line)
        numLines += 1
    EOF
    )
    zhead() { python -c "$zhead_py" "$@"; }
    

    ...它会让你zhead在输入数据用尽时不会失败,但是 通过失败的退出状态以获得真正的I / O失败或其他突发事件。 (用法的格式为zhead in.gz 5,从in.gz读取5行。

答案 1 :(得分:0)

或者,您可以使用

zcat file.gz  | awk '(NR<=12)'

价格是您需要遍历所有zcat,没有根据您指定的行提早止损。