Как использовать для, найти и подстановочный знак в Makefile? - PullRequest
0 голосов
/ 18 сентября 2018

Я пытаюсь сделать следующее безуспешно:

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

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Короткий ответ: вы не можете этого сделать.

При написании рецептов makefile очень важно четко понимать, как они работают: во-первых, make расширит все переменные и функции make.Затем make передает полученную строку в оболочку для запуска.Наконец, make ждет, пока оболочка завершит работу, и проверяет код завершения, чтобы выяснить, успешно ли он выполнен.

Здесь вы пытаетесь использовать цикл оболочки for, где тело цикла вызывает makewildcard функция.Это не может работать.

Подумайте, что вы хотите, чтобы make (и оболочка) делали: оболочка запускает свой цикл for, затем каждый раз, когда она выполняет тело цикла, она связывается обратно, чтобы получить значениенекоторой переменной оболочки, и make расширит функцию подстановки с использованием этой переменной, а затем вернет результаты в оболочку для дальнейшей обработки.Это просто невозможно.

Лучше всего написать рецепт с использованием конструкций shell .Хорошее эмпирическое правило заключается в том, что никогда не стоит использовать в рецепте функцию make shell: рецепт уже запускается в оболочке, поэтому функция shell вообще не нужна.Это просто сбивает с толку.Точно так же вам редко нужна функция make wildcard в рецепте, потому что оболочка автоматически выполняет глобализацию файлов для вас.

Я бы написал так:

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

Обратите внимание, что мы сбежали $$file с двумя знаками доллара, чтобы make не расширял его как переменную make.

0 голосов
/ 18 сентября 2018

Если я правильно понял, вы хотите удалить все файлы с именем:

src/**/migrations/0*.py

, где ** может быть чем угодно, включая вообще ничего.find может сделать это в одиночку:

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

И, поскольку make recipes - это просто пустая оболочка, вам не нужно $(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 для удаления файлов без подтверждения.

EDIT 1 : если это упражнение о циклах и make, просто помните, что рецепты make - это скрипты оболочки (по одному на строку), которые сначала раскрываются make, а затем передаются в оболочку.Итак:

  1. Если вы хотите использовать переменные оболочки, используйте их в одну строку (но вы можете разбивать строки с помощью завершающего \).В противном случае они будут происходить из разных вызовов оболочки, и это не будет работать должным образом.
  2. Если вы используете знак $ в своем рецепте расширения оболочки, удвойте их, чтобы избежать первого расширения путем make.

В дальнейшем я предполагаю, что у вас нет имен каталогов или файлов с пробелами или специальными символами.Если вы это сделаете, пришло время задать вопрос о вашей оболочке.

Итак, с помощью оболочки bash, например, мы можем сначала создать массив целевых каталогов, а затем зациклить их, чтобы удалить файлы:

SHELL := bash

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

EDIT 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 - путь к каталогу.И эти правила статические правила .В рецепте автоматическая переменная $* расширяется на make как основа шаблона clean-%.Таким образом, если DIR равно src/foo/bar/migrations, правило для него эквивалентно:

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

Основное преимущество этого стиля перед циклом оболочки состоит в том, что make может запускать все рецепты параллельно.Если у вас есть сотни migrations каталогов, и если у вас 8 или 16 ядер на вашем компьютере, это действительно может иметь значение.Просто попробуйте:

make -j1 remove
make -j16 remove

, и вы увидите разницу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...