管道传输到外壳中的多个文件


29

我有一个应用程序,该应用程序会产生大量不希望存储到磁盘上的数据。该应用程序主要输出我不希望使用的数据,但是输出的一组有用信息必须拆分为单独的文件。例如,给出以下输出:

JUNK
JUNK
JUNK
JUNK
A 1
JUNK
B 5
C 1
JUNK

我可以这样运行应用程序三遍:

./app | grep A > A.out
./app | grep B > B.out
./app | grep C > C.out

这可以让我得到想要的东西,但是会花费太长时间。我也不想将所有输出转储到单个文件中并通过它进行解析。

有什么方法可以将上面显示的三个操作组合在一起,使我只需要运行一次应用程序,仍然可以获得三个单独的输出文件?

Answers:


78

如果您有T恤

./app | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null

(从这里

关于工序替换


4
./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
太棒了

7
给定问题的原始标题“通往多个流程”,该答案是目前唯一准确的答案。
2013年

3
+1。这是最通用的答案,因为它不取决于特定过滤命令为的事实grep
ruakh 2013年

1
我同意这是对提出的问题的最佳答案,因此应标明。 并行是另一个解决方案(已发布),但是在进行了一些定时比较之后,以上示例更加有效。如果op涉及大量cpu密集型操作,例如多个文件压缩或多个mp3转换,那么毫无疑问,并行解决方案应该更有效。
AsymLabs

32

您可以使用 awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'

6
问题的标题是管道到多个进程,这个答案是关于“管道化”(通过正则表达式调度)到多个文件。由于此答案已被接受,因此应相应更改问题的标题。
2013年

@PauloMadeira你是对的。您认为哪个标题更好?
sj755

我建议做一个很小的编辑“将管道放置到外壳中的多个文件中”,这是未决的修订,请检查一下。我希望删除该评论(如果被接受)。
2013年

@PauloMadeira-我已更改标题。没有看到您的编辑,但您是正确的,如果这是可接受的答案,则标题中使用的处理不正确。
slm

17

您还可以使用shell的模式匹配功能

./app | while read line; do 
     [[ "$line" =~ A ]] && echo $line >> A.out; 
     [[ "$line" =~ B ]] && echo $line >> B.out; 
     [[ "$line" =~ C ]] && echo $line >> C.out; 
 done

甚至:

./app | while read line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && echo $line >> "$foo".out; 
  done; done

一种更安全的方法,可以处理以反斜杠和反斜杠开头的行-

./app | while IFS= read -r line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && printf -- "$line\n" >> "$foo".out; 
  done; done

正如@StephaneChazelas在评论中指出的那样,这不是很有效。最好的解决方案可能是@AurélienOoms'


这假设输入不包含这样开始的反斜杠或空格或通配符,或行-n-e......它也将是非常低效的,因为它意味着每行多个系统调用(一个read(2)每个字符,文件被打开,写作每行关闭...)。通常,使用while read循环在Shell中处理文本是一种不好的做法。
斯特凡Chazelas

@StephaneChazelas我编辑了答案。现在应该可以使用反斜杠-n等。据我所知,两个版本都可以用空格正常运行,我错了吗?
terdon

不,第一个参数printf是格式。没有理由将变量保留在那里。
斯特凡Chazelas

如果输入中包含null,这也会破坏bash(以及其他以类似方式使用cstring的shell)。
克里斯·

9

如果您有多个核心,并且希望进程是并行的,则可以执行以下操作:

parallel -j 3 -- './app | grep A > A.out' './app | grep B > B.out' './app | grep C > C.out'

这将在并行内核中产生三个进程。如果要向控制台或主文件提供一些输出,则它的优点是使输出保持一定顺序,而不是混合输出。

的GNU工具平行从奥莱丹下可以从下名最回购来获得并行moreutils。来源可从Savannah.gnu.org获得。另外一个介绍教学视频是在这里

附录

使用更新的并行版本(不一定是您的发行版仓库中的版本),您可以使用更优雅的构造:

./app | parallel -j3 -k --pipe 'grep {1} >> {1}.log' ::: 'A' 'B' 'C'

这样可以达到在单独的内核或线程中运行一个./app和3个并行grep进程的结果(由并行本身确定,也将-j3视为可选的,但在此示例中提供该值是出于指导目的)。

通过执行以下操作可以获得较新版本的parallel:

wget http://ftpmirror.gnu.org/parallel/parallel-20131022.tar.bz2

然后通常的解压缩,cd到parallel- {date},。/ configure && make,sudo make install。这将安装并行,手册页并行和手册页parallel_tutorial。


7

这是Perl中的一个:

./app | perl -ne 'BEGIN {open(FDA, ">A.out") and 
                         open(FDB, ">B.out") and 
                         open(FDC, ">C.out") or die("Cannot open files: $!\n")} 
                  print FDA $_ if /A/; print FDB $_ if /B/; print FDC $_ if /C/'

1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

...如果<in可读,则在将任何内容写入文件之前,所有三个文件都将被截断。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.