Многострочные команды bash в make-файле


121

У меня есть очень удобный способ скомпилировать проект с помощью нескольких строк команд bash. Но теперь мне нужно скомпилировать его через make-файл. Учитывая, что каждая команда запускается в собственной оболочке, мой вопрос: как лучше всего запустить многострочную команду bash, зависящую друг от друга, в make-файле? Например, вот так:

for i in `find`
do
    all="$all $i"
done
gcc $all

Кроме того, может кто-нибудь объяснить, почему даже однострочная команда bash -c 'a=3; echo $a > file'работает правильно в терминале, но создает пустой файл в случае файла makefile?


4
Вторая часть должна быть отдельным вопросом. Добавление еще одного вопроса просто создает шум.
l0b0

Ответы:


137

Для продолжения строки можно использовать обратную косую черту. Однако обратите внимание, что оболочка получает всю команду, объединенную в одну строку, поэтому вам также необходимо завершить некоторые строки точкой с запятой:

foo:
    for i in `find`;     \
    do                   \
        all="$$all $$i"; \
    done;                \
    gcc $$all

Но если вы просто хотите взять весь список, возвращенный findвызовом, и передать его gcc, вам на самом деле не обязательно нужна многострочная команда:

foo:
    gcc `find`

Или, используя более традиционный $(command)подход к оболочке (обратите внимание на $экранирование):

foo:
    gcc $$(find)

2
Для многострочности вам все равно нужно избегать знаков доллара, как указано в комментариях в другом месте.
Tripleee

1
Да, короткий ответ 1. $: = $$ 2. добавить многострочность с помощью \ BTW, ребята из bash обычно рекомендуют заменять вызов `find` на $$ (find)
Евгений Якимович

89

Как указано в вопросе, каждая подкоманда запускается в собственной оболочке . Это делает написание нетривиальных сценариев оболочки немного беспорядочным - но это возможно! Решение состоит в том, чтобы объединить ваш скрипт в одну подкоманду (одну строку), которую make будет рассматривать.

Советы по написанию сценариев оболочки в make-файлах:

  1. Чтобы избежать использования сценария, $заменив его на$$
  2. Преобразуйте скрипт, чтобы он работал как одну строку, вставив ;между командами
  3. Если вы хотите написать сценарий на нескольких строках, экранируйте конец строки с помощью \
  4. Необязательно начать с, set -eчтобы соответствовать положению make для прерывания при сбое подкоманды
  5. Это совершенно необязательно, но вы можете заключить скрипт в скобки ()или {}подчеркнуть связность многострочной последовательности - это не типичная последовательность команд make-файла.

Вот пример, вдохновленный OP:

mytarget:
    { \
    set -e ;\
    msg="header:" ;\
    for i in $$(seq 1 3) ; do msg="$$msg pre_$${i}_post" ; done ;\
    msg="$$msg :footer" ;\
    echo msg=$$msg ;\
    }

4
Вы также можете захотеть SHELL := /bin/bashвключить в свой make-файл специфические для BASH функции, такие как подстановка процесса .
Брент Брэдберн

8
Тонкий момент: пробел после {важен для предотвращения интерпретации {setкак неизвестной команды.
Брент Брэдберн

3
Тонкий момент №2: в make-файле это, вероятно, не имеет значения, но разница между оберткой {}и ()имеет большое значение, если вы иногда хотите скопировать сценарий и запустить его прямо из приглашения оболочки. Вы можете нанести ущерб своему экземпляру оболочки, объявив переменные и особенно изменив состояние с помощью setвнутри {}. ()предотвращает изменение сценарием вашей среды, что, вероятно, предпочтительнее. Пример ( это конец вашей оболочки сессии ): { set -e ; } ; false.
Брент Брэдберн,

Вы можете включать комментарии, используя следующую форму command ; ## my comment \` (the comment is between :; `и` \ `). Кажется, это работает нормально, за исключением того, что если вы запустите команду вручную (путем копирования и вставки), история команд будет включать комментарий таким образом, что команда прервется (если вы попытаетесь использовать его повторно). [Примечание: выделение синтаксиса для этого комментария нарушено из-за использования обратной косой черты внутри обратного апострофа.]
Брент Брэдберн,

Если вы хотите разбить строку в bash / perl / script внутри make-файла, закройте кавычку строки, обратную косую черту, новую строку (без отступа), открыв кавычку строки. Пример; perl -e QUOTE print 1; QUOTE BACKSLASH NEWLINE QUOTE print 2 QUOTE
mosh

2

Что плохого в простом вызове команд?

foo:
       echo line1
       echo line2
       ....

И для вашего второго вопроса вам нужно избежать $ , используя $$вместо этого, то есть bash -c '... echo $$a ...'.

РЕДАКТИРОВАТЬ: ваш пример можно переписать в однострочный скрипт следующим образом:

gcc $(for i in `find`; do echo $i; done)

5
Причина «каждая команда запускается в своей собственной оболочке», а мне нужно использовать результат command1 в command2. Как в примере.
Джофси

Если вам нужно распространять информацию между вызовами оболочки, вам необходимо использовать какую-либо форму внешнего хранилища, например временный файл. Но вы всегда можете переписать свой код для выполнения нескольких команд в одной оболочке.
JesperE

+1, особенно для упрощенной версии. И разве это не то же самое, что просто выполнить команду gcc `find`?
Эльдар Абусалимов

1
Да, это так. Но вы, конечно, можете делать более сложные вещи, чем просто «эхо».
JesperE

2

Конечно, правильный способ написать Makefile - это фактически задокументировать, какие цели зависят от источников. В тривиальном случае предлагаемое решение будет fooзависеть от самого себя, но, конечно, makeдостаточно умен, чтобы отказаться от циклической зависимости. Но если вы добавите временный файл в свой каталог, он «волшебным образом» станет частью цепочки зависимостей. Лучше раз и навсегда создать явный список зависимостей, возможно, с помощью скрипта.

GNU Make знает , как работать , gccчтобы получить исполняемый из набора из .cи .hфайлов, так что, может быть , все , что вам действительно нужно , чтобы количество

foo: $(wildcard *.h) $(wildcard *.c)

1

Директива ONESHELL позволяет писать несколько строковых рецептов, которые будут выполняться при одном вызове оболочки.

SOURCE_FILES = $(shell find . -name '*.c')

.ONESHELL:
foo: ${SOURCE_FILES}
    for F in $^; do
        gcc -c $$F
    done

Однако есть недостаток: специальные символы префикса ('@', '-' и '+') интерпретируются по-разному.

https://www.gnu.org/software/make/manual/html_node/One-Shell.html

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.