在程序之间传输多个文件的内容,同时保持内容独立

在程序之间传输多个文件的内容,同时保持内容独立

我想读入许多文件并将它们的输出通过管道传输到后续程序,同时仍然将它们维护为单独的数据管道。

program1 *.txt | program2 | program3 folder

我知道上述语法可以为单个数据流完成什么,但我正在考虑在整个操作过程中保持文件独立。上述内容将转化为以下内容:

  1. 程序1读取文本文件并通过管道传输到程序2
  2. 程序2单独处理数据并通过管道传输到程序3
  3. program3 将数据写入与原始文件名相同的文件夹中的文件

这种操作目前是像 Gulp 这样的构建工具的领域,但我正在尝试看看 shell 是否可以完全取代它们。由于编写的程序只能处理一个stdin,因此这似乎不可行。

多个文件的读取和写入不是问题,因为我将在程序本身中处理这个问题。

我已经研究了以下内容,但它们似乎不是正确的解决方案:

  • 命令tee
  • 文件描述符
  • 替换

一种可能的方法是为每个单独的文件创建一个进程,并在某处维护文件名列表,但我希望有更优雅的方法。

答案1

就像任何文件一样,管道是文本流(更准确地说是字节流)。 Unix 的基本构建块往往很简单。流程之间的交互主要基于非结构化数据。操作系统不提供具有由文件名标记的多个流的通信通道。如果程序需要这样做,他们需要安排自己的管道,每个流一个管道,将是最自然的实现。

如果program2program3独立地作用于每个流,则为每个文件运行它们的一个副本。要按顺序运行它们,请使用 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

如果您需要单个实例program2program3处理多个文件,则需要使用自定义接口来设计这些程序以接收多个管道作为输入。没有标准方法可以做到这一点。一种方法是让他们调用提供输入的程序。它的方式xargsparallel被告知调用哪个程序来处理其输出的方式类似。

答案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

相关内容