代码之家  ›  专栏  ›  技术社区  ›  Natim

如何在生成文件中使用for、find和通配符?

  •  1
  • Natim  · 技术社区  · 6 年前

    到目前为止,我一直在努力做到以下几点:

    remove:
        for file in $(shell find src/ -name migrations -type d); do rm $(wildcard "$(file)/0*.py"); done
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   Renaud Pacalet    6 年前

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

    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. 如果您使用 $ 在你的shell扩展食谱上签名,将它们翻两倍以避免make的第一次扩展。

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

    因此,例如,使用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循环系统与一个(假的)一起使用。 清洁的 每个目录的目标。

    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-% 模式。所以,如果 迪尔 src/foo/bar/migrations ,其规则等同于:

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

    这种样式相对于shell循环的主要优点是make可以并行运行所有配方。如果你有上百个 迁徙 如果您的计算机上有8或16个内核,那么它真的可以起到作用。试一试:

    make -j1 remove
    make -j16 remove
    

    你会看到不同。

        2
  •  0
  •   MadScientist    6 年前

    简而言之,你不能这样做。

    在编写makefile配方时,必须清楚了解它们的工作原理:首先,make将扩展所有make变量和函数。然后,make将把生成的字符串传递给要运行的shell。最后,make等待直到shell完成并检查退出代码,以确定它是否成功。

    这里你想用贝壳 for 循环,其中循环体调用make wildcard 功能。这行不通。

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

    最好是用 构造。一个好的经验法则是,使用make的方法从来不是一个好主意。 shell 配方内的函数:配方已经在shell中运行,因此您不需要 功能。这简直令人困惑。同样,你很少需要make的 通配符 在配方中运行,因为shell会自动为您进行文件填充。

    我写这个是:

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

    注意我们逃走了 $$file 有两个美元的标志,以防止制造业扩大它作为一个制造变量。