我想读入许多文件并将它们的输出通过管道传输到后续程序,同时仍然将它们维护为单独的数据管道。
program1 *.txt | program2 | program3 folder
我知道上述语法可以为单个数据流完成什么,但我正在考虑在整个操作过程中保持文件独立。上述内容将转化为以下内容:
- 程序1读取文本文件并通过管道传输到程序2
- 程序2单独处理数据并通过管道传输到程序3
- program3 将数据写入与原始文件名相同的文件夹中的文件
这种操作目前是像 Gulp 这样的构建工具的领域,但我正在尝试看看 shell 是否可以完全取代它们。由于编写的程序只能处理一个stdin
,因此这似乎不可行。
多个文件的读取和写入不是问题,因为我将在程序本身中处理这个问题。
我已经研究了以下内容,但它们似乎不是正确的解决方案:
- 命令
tee
- 文件描述符
- 替换
一种可能的方法是为每个单独的文件创建一个进程,并在某处维护文件名列表,但我希望有更优雅的方法。
答案1
就像任何文件一样,管道是文本流(更准确地说是字节流)。 Unix 的基本构建块往往很简单。流程之间的交互主要基于非结构化数据。操作系统不提供具有由文件名标记的多个流的通信通道。如果程序需要这样做,他们需要安排自己的管道,每个流一个管道,将是最自然的实现。
如果program2
和program3
独立地作用于每个流,则为每个文件运行它们的一个副本。要按顺序运行它们,请使用 shell 循环。与管道一样,循环是将程序连接在一起的 shell 功能之一。为了告诉program3
将输出放在哪里,通常的接口是program3
写入其标准输出,并在 shell 中使用重定向结构将输出定向到文件。 shell 提供了一些基本的字符串操作结构来构建文件名;这里只是串联。
for x in *.txt; do
program1 "$x" | program2 | program3 >"folder/$x"
done
如果程序的 IO 消耗较少,但 CPU 密集型,并且您有多个 CPU,您可能希望并行运行它们。有了足够新的 GNU 工具,您可以使用xargs
并行运行程序。将系统上的 CPU 数量作为参数传递给-P
。由于需要执行的命令xargs
是管道,因此需要使其调用 shell。
find -maxdepth 1 -name '*.txt' -print0 |
xargs -0 -n 1 -P 4 sh -c 'program1 "$1" | program2 | program3 >"$0/$1"' "folder"
您可以使用GNU并行而不是 xargs 让它自动确定系统上的 CPU 数量。
parallel sh -c 'program1 "$1" | program2 | program3 >"$0/$1"' "folder" ::: *.txt
如果您需要单个实例program2
并program3
处理多个文件,则需要使用自定义接口来设计这些程序以接收多个管道作为输入。没有标准方法可以做到这一点。一种方法是让他们调用提供输入的程序。它的方式xargs
与parallel
被告知调用哪个程序来处理其输出的方式类似。
答案2
你在谈论吗
program1 file1.txt | program2 | program3 > folder/file1.txt
program1 file2.txt | program2 | program3 > folder/file2.txt
program1 file42.txt | program2 | program3 > folder/file42.txt
program1 green.txt | program2 | program3 > folder/green.txt
program1 indigo.txt | program2 | program3 > folder/indigo.txt
program1 leopard.txt | program2 | program3 > folder/leopard.txt
program1 lion.txt | program2 | program3 > folder/lion.txt
⋮ ⋮ ⋮ ⋮ ⋮
?
你可以这样做
for f in file1.txt file2.txt file42.txt green.txt indigo.txt leopard.txt lion.txt ...
do
program1 "$f" | program2 | program3 > folder/"$f"
done
如果您想对当前目录中的所有文本文件执行此操作,只需使用通配符(又名“glob”):
for f in *.txt
do
program1 "$f" | program2 | program3 > folder/"$f"
done