我想计算管道中的行数,然后根据结果继续管道。
我试过了
x=$(printf 'faa\nbor\nbaz\n' \
| tee /dev/stderr | wc -l) 2>&1 \
| if [[ $x -ge 2 ]]; then
grep a
else
grep b
fi
但它根本不过滤(“a”和“b”都没有)。这是非常出乎意料的,因为至少这些工作符合预期:
printf 'faa\nbor\nbaz\n' | if true; then grep a; else grep b; fi
printf 'faa\nbor\nbaz\n' | if false; then grep a; else grep b; fi
似乎我无法从命令替换内部重定向标准错误,因为这也不起作用(在 bash 中)。它打印所有三行:
x=$(printf 'faa\nbor\nbaz\n' | tee /dev/stderr | wc -l) 2>&1 | grep a
在 zsh 中它只打印两行。
但是在两个 shell 中,变量 x 在管道之后都没有设置,甚至在管道的后半部分也没有设置。
我该怎么做才能计算管道中的行数,然后根据该数字采取行动?我想避免使用临时文件。
这个评论是真的:
这并不意味着你不能做任何事情。管道可能被认为是主要的数据通道,但进程仍然可以使用侧通道进行通信:文件、命名 fifo 或其他任何东西(尽管有时您需要格外小心,不要让它们阻塞)。
您想计算行数并稍后有条件地处理整个数据流。这意味着您需要到达流的末尾,然后才能传递整个流。所以你需要以某种方式保存整个流。临时文件看起来像是一种理智的方法。您应该将管道分成至少两部分。第一部分应将数据保存在文件中;然后应该计算行数(我认为这个任务可能属于第一部分);然后最后一部分应该得到数字,从头开始读取文件以接收数据,并采取相应的行动。
如果您真的想避免使用临时文件,那么您的管道的某些部分应该以某种方式表现得像
sponge
. 为避免旁通道,应将行数作为输出的第一行传递,并且管道的其余部分应理解此协议。考虑这个命令:
它在保持空间中累积线。如果至少有一行,则在收到最后一行后
sed
打印行数,然后是空行和实际输入。空行是不必要的,但从这个简单的代码中“自然”地出现。我不会试图在 中避免它
sed
,而是稍后在管道中处理它(例如,使用sed '2 d'
)。示例用法:
笔记:
IFS= read -r
是一个矫枉过正,因为第一行定义明确,它包含一个唯一的数字(或它不存在)。/bin/sh
。该代码也将在 Bash 中运行。您不能假设
sed
能够保存任意数量的数据。POSIX 规范说:所以它的限制可能只有 8192 字节。另一方面,我可以想象一个临时文件很容易保存 1TB 的数据。也许不要不惜一切代价避免临时文件。
标题说“计算行数”,但您的示例试图确定数字是否为 2 或更多(通常为 N 或更多)。这些问题是不等价的。在输入第二(N)行之后,您知道后一个问题的答案,甚至行将无限期地出现。上面的代码不能处理不确定的输入。让我们在某种程度上修复它。
此命令的行为与之前的解决方案类似,但当它到达第 6 行时,它假定(打印)行数为
6+
. 然后打印已经看到的行,并在它们出现后立即打印以下行(如果有的话)(类似cat
行为)。示例用法:
笔记:
sed
(无论您的情况是什么限制)的限制仍然适用。但是现在sed
最多处理几$threshold
行;如果$threshold
足够低,那么应该没问题。$threshold+
但协议允许您区分 0、1、2、...、阈值减一和阈值或更多行。我不是很熟练
sed
。如果我的sed
代码可以简化,请在评论中给我提示。根据 Kamil 的讨论和 sed 代码,我找到了这个 awk 解决方案: