Багаторядкові команди bash у makefile


120

У мене дуже зручний спосіб скласти свій проект через кілька рядків команд bash. Але тепер мені потрібно скомпілювати його через makefile. Враховуючи, що кожна команда виконується у власній оболонці, моє питання - який найкращий спосіб запустити багаторядкові команди bash, залежні один від одного, у makefile? Наприклад, наприклад:

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

Також хтось може пояснити, чому навіть однорядкова команда bash -c 'a=3; echo $a > file'працює коректно в терміналі, але створює порожній файл у випадку makefile?


4
Друга частина повинна бути окремим питанням. Додавання іншого питання просто створює шум.
l0b0

Відповіді:


136

Ви можете використовувати зворотну косу рису для продовження рядка. Однак зауважте, що оболонка отримує всю команду, об'єднану в один рядок, тому вам також потрібно припинити деякі рядки крапкою з комою:

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

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

foo:
    gcc `find`

Або, скориставшись більш звичайним $(command)підходом до оболонки (зауважте, що $втеча):

foo:
    gcc $$(find)

2
Для багатолінійки вам все-таки потрібно уникати знаків долара, як зазначено в коментарях в інших місцях.
трійка

1
Так, коротка відповідь 1. $: = $$ 2. додайте багаторядковий \ BTW баш хлопці зазвичай рекомендують замінити дзвінок `find` на $$ (знайти)
Яухен Якимович

89

Як зазначено в питанні, кожна підкоманда виконується у власній оболонці . Це робить сценарії нетривіальних оболонок трохи безладними - але це можливо! Рішення полягає в тому, щоб закріпити свій сценарій у тому, що make буде враховувати одну підкоманду (єдиний рядок).

Поради щодо написання скриптів оболонки в makefiles:

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

Ось приклад, натхненний ОП:

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у своєму makefile увімкнути особливості BASH, такі як підміна процесу .
Брент Бредберн

8
Тонкий пункт: Пробіл після {має вирішальне значення для запобігання інтерпретації {setяк невідомої команди.
Брент Бредберн

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

Ви можете включити коментарі, скориставшись такою формою: command ; ## my comment \` (the comment is between `і` \ `). Здається, це працює добре, за винятком того, що якщо ви запускаєте команду вручну (копіюючи та вставляючи), історія команд буде включати коментар таким чином, що порушує команду (якщо ви спробуєте її повторно використати). [Примітка: Підсвітка синтаксису порушена для цього коментаря через використання зворотної косої риси у зворотній частині.]
Brent Bradburn

Якщо ви хочете перервати рядок у bash / perl / script всередині makefile, закрийте цитату рядка, зворотну косу рису, новий рядок (без відступу) відкрийте цитату рядка. Приклад; perl -e ЦІНА друк 1; ЦИТАТИ БАКСЛАШ НОВИНИ ЦІТІ друкувати 2 ЦІТА
мош

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.