首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用空管道和管道对管道进行分叉

用空管道和管道对管道进行分叉
EN

Stack Overflow用户
提问于 2018-07-10 03:30:28
回答 2查看 556关注 0票数 1

我有一个流是空分隔的,有一个未知的节数。对于每个被分隔的部分,我希望将其输送到另一个管道中,直到读取最后一节为止,然后终止。实际上,每个部分都很大(~1GB),所以我想这样做,而不必将每个部分读入内存。

例如,假设我创建的流是:

代码语言:javascript
复制
for I in {3..5}; do seq $I; echo -ne '\0'; 
done

我会得到一个看起来像:

代码语言:javascript
复制
1
2
3
^@1
2
3
4
^@1
2
3
4
5
^@

通过管道通过cat -v

我想通过paste -sd+ | bc传输每个部分,因此我得到了一个类似于以下内容的流:

代码语言:javascript
复制
6
10
15

这只是一个简单的例子。实际上,流更大,管道更复杂,所以不依赖流的解决方案是不可行的。

我试过这样的方法:

代码语言:javascript
复制
set -eo pipefail
while head -zn1 | head -c-1 | ifne -n false | paste -sd+ | bc; do :; done

但我只得到

代码语言:javascript
复制
6
10

如果我停止了bc,我会得到

代码语言:javascript
复制
1+2+3
1+2+3+4
1+2+3+4+5

这基本上是正确的。这使我相信,这个问题可能与缓冲有关,以及每个进程实际上与它们之间的管道交互的方式。

是否有什么方法可以修复这些命令交换流的方式,以便我能够获得所需的输出?或者,是否有其他方法来实现这一目标?

原则上,这与this question有关,我当然可以编写一个程序,将stdin读入缓冲区,查找空字符,并将输出传递到生成的子进程,就像该问题的公认答案所做的那样。考虑到bash中对流和空分隔符的普遍支持,我希望做一些更“本地”的事情。特别是,如果我想走这条路,我必须用字符串转义管道(paste -sd+ | bc),而不是让同一个shell解释它。从本质上讲,这并没有什么不好的地方,但它有点丑,需要一堆容易发生错误的逃逸。

编辑

正如在一个答案中所指出的,head不能保证它的缓冲量。除非它一次只能缓冲单个字节,这是不切实际的,否则这是行不通的。因此,似乎唯一的解决方案是将其读入内存或write a specific program

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-07-11 01:54:03

如前所述,唯一真正的解决方案似乎是专门编写一个程序来实现这一点。我用铁锈写了一个叫做xstream-util。在用cargo install xstream-util安装它之后,您可以将输入管道输送到

代码语言:javascript
复制
xstream -0 -- bash -c 'paste -sd+ | bc'

以获得所需的输出

代码语言:javascript
复制
6
10
15

它并不能避免在bash中运行程序,所以如果管道很复杂,它仍然需要转义。此外,它目前只支持单字节分隔符。

票数 1
EN

Stack Overflow用户

发布于 2018-07-10 04:00:32

您的原始代码的问题是,head不能保证它不会读取比输出更多的内容。因此,它可以使用多个(以NUL分隔的)输入块,即使它只发出一个输出块。

相比之下,read保证它的消耗不会超过您的要求。

代码语言:javascript
复制
set -o pipefail
while IFS= read -r -d '' line; do
  bc <<<"${line//$'\n'/+}"
done < <(build_a_stream)

如果您想要本机逻辑,那么没有什么比用shell编写整个程序更简单的了。

调用外部工具--包括bccutpaste或其他工具--需要fork()惩罚。如果每次调用只处理少量数据,那么这些工具的效率就会被启动它们的成本所淹没。

代码语言:javascript
复制
while read -r -d '' -a numbers; do  # read up to the next NUL into an array
  sum=0                             # initialize an accumulator
  for number in "${numbers[@]}"; do # iterate over that array
    (( sum += number ))             # ...using an arithmetic context for our math
  done
  printf '%s\n' "$sum"
done < <(build_a_stream)

对于上述所有内容,我使用以下build_a_stream实现进行了测试:

代码语言:javascript
复制
build_a_stream() {
  local i j IFS=$'\n'
  local -a numbers
  for ((i=3; i<=5; i++)); do
    numbers=( )
    for ((j=0; j<=i; j++)); do
      numbers+=( "$j" )
    done
    printf '%s\0' "${numbers[*]}"
  done
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51256841

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档