我正在编写 Makefile 来维护工作流程。工作流程的主要部分需要一个可以处理文件对的程序。我有一个包含原始文件对的原始数据目录。每对都有一个名称,后跟
_1.txt
或 _2.txt
。程序的第一步应该运行一个 QC 管道,该管道需要这两个对并产生新的对。该命令如下所示:
run_qc.sh -in1 file_1.txt -in2 file_2.txt -out1 file_1.qc.txt -out2 file_2.qc.txt
第二步采用质量控制文件并将它们组合成最终结果,如下所示:
run_analysis.sh file_1.qc.txt file_2.qc.txt > file_result.txt
我希望程序针对具有相同前缀的所有对运行(即上例中的
file
是前缀)。到目前为止,我想到的最好的方法是只查看其中一个文件对,然后假设第二个文件对的所有内容都有效,并制定一个虚拟规则,该规则不执行任何操作来填充依赖关系图。看起来像这样:
RAW_DIR = data/raw
QC_DIR = data/qc
ALL_FILES = $(wildcard $(RAW_DIR)/*.txt)
QC_FILES = $(patsubst $(RAW_DIR)/%.txt, $(QC_DIR)/%.qc.txt, $(ALL_FILES))
qc : $(QC_FILES)
$(QC_DIR)/%_1.qc.txt : $(RAW_DIR)/%_1.txt
RAW1 = $<
RAW2 = $(patsubst $(RAW_DIR)/%_1.txt, $(RAW_DIR)/%_2.txt)
QC1 = $@
QC2 = $(patsubst $(QC_DIR)/%_1.qc.txt, $(QC_DIR)/%_2.qc.txt)
run_qc.sh -in1 $(RAW1) -in2 $(RAW2) -out1 $(QC1) -out2 $(QC2)
$(QC_DIR)/%_2.qc.txt : $(RAW_DIR)/%_2.txt
@echo
这个解决方案似乎工作正常,但我认为我缺少一些东西来使工作流程更易于维护,而无需额外的无用目标。我还假设,一旦弄清楚这一步,将结果合并到最终分析步骤中就会容易得多。
所以,最终我的问题是,当我可能必须在许多文件对上运行这个规则时,如何制定一个能够识别这些文件对的规则?
提前致谢,
您显然认为制作食谱是用“制作语言”之王编写的。不是这种情况。菜谱是 shell 脚本,而您的菜谱不是有效的 shell 语法(
RAW1 = $<
应该是 RAW1=$<
)。
此外,配方的所有行都由不同的 shell 执行。你不能在一行中分配一个变量并在另一行中使用它......除非你将完整的配方编写为单行 shell,如果你愿意的话,还可以继续行。
此外,make 在将配方传递到 shell 之前会扩展它们。因此,如果您希望将
$
符号传递给 shell,则必须转义它 ($$
),这样,在通过 make 扩展后,它会变成 $
。
此外,
patsubst
需要3个参数,而不是两个。但你不需要它。 $*
自动变量是你的朋友。
所以,你的规则应该是:
$(QC_DIR)/%_1.qc.txt $(QC_DIR)/%_2.qc.txt: $(RAW_DIR)/%_1.txt $(RAW_DIR)/%_2.txt
run_qc.sh -in1 $(RAW_DIR)/$*_1.txt -in2 $(RAW_DIR)/$*_2.txt -out1 $(QC_DIR)/$*_1.qc.txt -out2 $(QC_DIR)/$*_1.qc.txt
GNU make 具有多个目标的模式规则很特殊。它们作为分组目标工作,并且配方仅执行一次即可构建所有目标。使用最新版本的 GNU make,您甚至可以明确这一点:
$(QC_DIR)/%_1.qc.txt $(QC_DIR)/%_2.qc.txt &: $(RAW_DIR)/%_1.txt $(RAW_DIR)/%_2.txt
run_qc.sh -in1 $(RAW_DIR)/$*_1.txt -in2 $(RAW_DIR)/$*_2.txt -out1 $(QC_DIR)/$*_1.qc.txt -out2 $(QC_DIR)/$*_1.qc.txt