在Makefile目标中处理xargs的当前输入字符串
解决Makefile中xargs路径解析的问题
你的问题出在Make的变量展开顺序上:Make会先把所有$(shell ...)这类Make语法展开,而xargs的{}是在xargs运行时才替换成实际文件路径的。这就导致你原来的$(shell basename $(dirname {}))里,{}是字面量,Make展开后得到的是basename ./,结果是.,自然没法得到正确的父目录名。
解决方案:把路径处理逻辑放到shell子进程中
修改xargs的执行命令,用bash -c包裹,让shell来处理路径解析(注意在Makefile里要把$转义为$$,避免Make提前展开):
dryrun: @mkdir -p $(OUTPUT_DIR) @if test -e $(MY_DIST); then \ source $(VENV_ACTIVATE) \ && list-files -0 $(MY_DIST) \ | xargs -0 -n1 -P12 -I{} bash -c ' \ base_dir=$$(basename $$(dirname "{}")); \ my-tool run --output-dir "$(OUTPUT_DIR)/$$base_dir" --dryrun; \ '; \ fi
原理说明
- 用
bash -c启动一个子shell,xargs会把{}替换为实际的文件绝对路径后传入这个子shell。 - 在子shell中,
$$(dirname "{}")会解析出文件的父目录路径,再用$$(basename ...)提取父目录的名称。 - 最后把这个名称和
$(OUTPUT_DIR)拼接,作为--output-dir的参数。
备选方案:用awk预处理路径
如果你不想依赖bash,也可以用awk先把每个文件路径和对应的父目录名一起输出,再用xargs批量处理:
dryrun: @mkdir -p $(OUTPUT_DIR) @if test -e $(MY_DIST); then \ source $(VENV_ACTIVATE) \ && list-files -0 $(MY_DIST) \ | awk -v RS='\0' '{print $$0 "\0" gensub(/.*\/([^\/]+)\/[^\/]+$$/, "\\1", 1, $$0)}' \ | xargs -0 -n2 -P12 -- my-tool run --output-dir "$(OUTPUT_DIR)/{}" --dryrun; \ fi
这个方案用awk的正则匹配提取父目录名,再和原路径一起以null分隔输出,xargs每次读取两个参数(路径和父目录名),直接替换到--output-dir中。不过正则需要确保输入是标准的绝对路径,适用性不如bash方案广泛。
内容的提问来源于stack exchange,提问作者Matt Heidelbaugh
相关产品推荐
相关产品推荐

