如何在Makefile中使用,查找和使用通配符?

时间:2018-09-18 12:56:54

标签: makefile

到目前为止,我正在尝试执行以下操作但未成功:

remove:
    for file in $(shell find src/ -name migrations -type d); do rm $(wildcard "$(file)/0*.py"); done

2 个答案:

答案 0 :(得分:1)

如果我理解得很好,您想删除所有名为:

的文件。
src/**/migrations/0*.py

其中**可以是任何东西,包括任何东西。 find可以单独执行此操作:

find src -path '*/migrations/0*.py' -delete

由于制作食谱只是裸露的外壳,因此不需要$(shell...或类似的东西:

remove:
    find src -path '*/migrations/0*.py' -delete

但是在运行此潜在危险规则之前,您可能应该使用此规则,直到满意为止:

remove:
    find src -path '*/migrations/0*.py' -ok rm {} \;

唯一的区别是它将在删除文件之前要求您确认。我们还可以使这一切变得简单:

remove-safe: DELCMD := -ok rm {} \\;
remove-unsafe: DELCMD := -delete
remove-dry-run: DELCMD := -print

remove-safe remove-unsafe remove-dry-run:
    find src -path '*/migrations/0*.py' $(DELCMD)

根据需要使用一个或另一个目标:

  • remove-dry-run以打印将要删除的文件列表,而不删除它们,
  • remove-safe删除带有确认的文件,
  • remove-unsafe删除未经确认的文件。

编辑1 :如果这是关于循环和make的练习,只需记住make配方是shell脚本(每行一个),首先由make扩展,然后再传递给shell。所以:

  1. 如果要使用shell变量,请在一行中使用它们(但可以在行尾加上\来换行)。否则,它们将来自不同的Shell调用,并且无法按预期工作。
  2. 如果您在配方中使用$符号进行外壳扩展,请将其加倍以按make进行第一个扩展。

在下文中,我假定您没有带有空格或特殊字符的目录或文件名。如果这样做,是时候问一个关于您的shell的问题了。

例如,使用bash shell,我们可以首先构建目标目录的数组,然后循环遍历它们以删除文件:

SHELL := bash

remove:
    dirs=( $$(find src -type d -name migrations) ); \
    for d in "$${dirs[@]}"; do \
        rm -f "$$d"/0*.py; \
    done

编辑2 :您还可以将内置make循环系统与每个目录一个(语音) clean 目标一起使用。

DIRS := $(shell find src -type d -name migrations)

clean-targets := $(addprefix clean-,$(DIRS))

.PHONY: remove $(clean-targets)

remove: $(clean-targets)

$(clean-targets): clean-%:
    rm -f "$*"/0*.py

棘手的部分是:

$(clean-targets): clean-%:
    rm -f "$*"/0*.py

规则。等效于每个migrations目录的一条规则为:

clean-DIR: clean-%:
    rm -f "$*"/0*.py

其中DIR是目录路径。这些规则是static pattern rules。在配方中,$* make自动变量由make扩展为clean-%模式的茎。因此,如果DIRsrc/foo/bar/migrations,则其规则等效于:

clean-src/foo/bar/migrations:
    rm -f src/foo/bar/migrations/0*.py

这种样式在shell循环上的主要优点是make可以并行运行所有配方。如果您有数百个migrations目录,并且计算机上具有8或16个内核,则确实可以有所作为。只需尝试:

make -j1 remove
make -j16 remove

您将看到区别。

答案 1 :(得分:0)

简短的答案是你不能这样做。

编写makefile配方以清楚了解它们的工作原理很关键:首先,make将扩展所有make变量和函数。然后,make会将结果字符串传递给要运行的shell。最后,make等待外壳程序完成,然后检查退出代码以查明它是否成功。

在这里,您尝试使用外壳程序for循环,其中循环主体调用make wildcard函数。这行不通。

考虑一下您要make(和shell)执行的操作:shell会运行其for循环,然后每次执行循环主体时,它都会进行通信以返回一些shell变量的值,并且make使用该变量扩展make通配符函数,然后将结果返回给Shell以进行更多处理。根本不可能。

最好是使用 shell 结构编写配方。一条好的经验法则是,在配方内部使用make的shell函数绝不是一个好主意:配方已经在外壳中运行,因此根本不需要shell函数。简直令人困惑。同样,您几乎不需要在食谱中使用make的wildcard函数,因为shell会自动为您提供文件glob。

我会这样写:

remove:
        find src/ -name migrations -type d | while read -r dir; do rm "$$file"/0*.py; done

请注意,我们用两个美元符号对$$file进行了转义,以防止make扩展为make变量。