Як перевірити, чи існує файл у Makefile, щоб я міг його видалити?


135

У чистому розділі Makefileя намагаюся перевірити, чи існує файл, перш ніж остаточно видалити. Я використовую цей код, але отримую помилки.

Що з цим не так?

 if [ -a myApp ]
 then
     rm myApp
 fi

Я отримую це повідомлення про помилку

 if [ -a myApp ]
 /bin/sh: Syntax error: end of file unexpected (expecting "then")
 make: *** [clean] Error 2

Чи мій myApp змінна чи власне ім'я файлу?
BugFinder

myApp призначений для myApplication, тобто імені файлу в процесі збирання.
Abruzzo Forte e Gentile

5
Якщо ви просто хочете уникнути зупинки, якщо файл не існує, це rm -rf myAppможе бути альтернативою. Або перед командою з тире ( -rm myApp), щоб зробити ігнорувати помилку з rm (проте вона буде друкувати некрасиве повідомлення).
thomasa88

1
Ваша проблема полягала в тому, що перетворюйте кожен рядок у праві як окрему команду та надсилає їх окремо до оболонки. Це як би запустити просто `if [-a myApp] 'самостійно. Якщо ви отримаєте цю помилку, вам або потрібне рішення, яке з'єднує рядки в один (використовуючи) або закінчується кожним рядком незалежно від іншого. Нижче їх декілька.
Майкл

Відповіді:


40

У другому верхньому відповіді згадується ifeq, однак він не зазначає, що вони повинні бути на тому ж рівні, що й ім'я цілі, наприклад, для завантаження файлу лише у тому випадку, якщо його наразі не існує, може бути використаний наступний код:

download:
ifeq (,$(wildcard ./glob.c))
    curl … -o glob.c
endif

# THIS DOES NOT WORK!
download:
    ifeq (,$(wildcard ./glob.c))
        curl … -o glob.c
    endif

не працювало, поки я не додав зворотну косу
рису

3
Ця відповідь буде трохи дивною; перевірка файлів відбувається під час обробки файлу makefile, але дія відбудеться пізніше, коли мета будується за допомогою make. Якщо ви тим часом видалите файл, файл не буде створений. Я вніс редагування, щоб зробити це зрозумілішим.
Майкл

Велике спасибі! Цей момент був незрозумілий з прочитання посібника.
Кевін

207

Дивно бачити так багато людей, які використовують для цього сценарії оболонок. Я шукав спосіб використовувати синтаксис власного makefile, тому що пишу це поза будь-якою ціллю. Ви можете скористатися wildcardфункцією, щоб перевірити, чи існує файл:

 ifeq ($(UNAME),Darwin)
     SHELL := /opt/local/bin/bash
     OS_X  := true
 else ifneq (,$(wildcard /etc/redhat-release))
     OS_RHEL := true
 else
     OS_DEB  := true
     SHELL := /bin/bash
 endif 

Оновлення:

Я знайшов спосіб, який дійсно працює для мене:

ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif

4
спробував це, але я просто продовжую отримуватиMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n

1
Прекрасне використання шаблону, тому це можна зробити з самим makefile.
Охайний

3
Допомагає також зрозуміти, що $(wildcard pattern)насправді робить. Дивіться посилання .
Доктор Ден

1
Більш лаконічний:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - відновлення моніки

2
Варто зауважити, якщо ви працюєте в Windows під cygwin, використовуючи підстановку таким чином, збігається з регістром ім'я файлу, тож якщо файл існує, але з іншим регістром, ніж шлях, він не знайдеться. Здається, немає ніякого способу змінити цю поведінку в makefile.
Роджер Сандерс

59

Проблема полягає в тому, що ви розділите свою команду на кілька рядків. Таким чином, ви можете використовувати або \в кінці рядків для продовження, як вище, або ви можете отримати все на одній лінії з &&оператором в bash.

Потім ви можете скористатися testкомандою, щоб перевірити, чи файл існує, наприклад:

test -f myApp && echo File does exist

-f file Щоправда, якщо файл існує та є звичайним файлом.

-s file Вірно, якщо файл існує та має розмір більше нуля.

чи ні:

test -f myApp || echo File does not exist
test ! -f myApp && echo File does not exist

Команда testеквівалентна [команді.

[ -f myApp ] && rm myApp   # remove myApp if it exists

і воно буде працювати як у вашому оригінальному прикладі.

Див.: help [Або help testдля подальшого синтаксису.


4
Я б підтримав, але ви не попередили, що -sце особлива справа exists and has a size greater than zero. Питання, як написано, має розмір-агностик, тому існування слід перевірити, використовуючи test -eфайл або -dкаталог. Порожні файли можуть бути особливо корисними як (для бажання кращого терміну) індикатори / дозорні, що може бути досить актуальним для make.
підкреслюй_d

Дякую за пропозицію. Змінено -fза замовчуванням, оскільки це звичайніше використання.
kenorb

Як я можу потрапити testна Windows?
Тхова

3
Для того, щоб це працювало, вам потрібно додати || trueв кінці, щоб команда повертала true, коли файл не існує.
jcubic

2
@AndrewMackenzie test -f myApp || CMD, зверніть увагу на ||, тому якщо -fне вдасться - не існує ( ||), тоді запустіть команду. Чи є сенс?
kenorb

50

Для продовження може знадобитися зворотний косий рядок на кінці рядка (хоча, можливо, це залежить від версії make):

if [ -a myApp ] ; \
then \
     rm myApp ; \
fi;

2
здається, це не синтаксис Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/…
holms

@holms - це синтаксис bash. За допомогою нових рядків це дозволяє обробляти його як єдину команду bash. За замовчуванням у makeновому рядку буде нова команда bash. Основним застереженням цього, окрім прикрості від наявності \ у кінці рядків, є те, що кожна команда повинна бути завершена тим, ;що інакше буде неявним у баші.
грипго

1
Правильна відповідь - від @holms. Ані "\", ані мальовничі символи точно не призначені для цієї мети. Причина, чому ви використовуєте підстановку, полягає в ясності коду. Цей метод не тільки менш читабельний, він більше схильний до синтаксичних помилок.
Майтрея,

1
посилання @holms надано більше не працює, замість цього використовуйте gnu.org/software/make/manual/make.html#Conditionss
fero

це чудова відповідь, оскільки він відповідає тому, що не так з тим, що робив оригінальний запитувач, і він буде працювати залежно від того, існує файл у момент створення цілі, а не в момент запуску Makefile, що саме очікує більшість людей і хочу більшу частину часу. У кількох дивних випадках відповідь від @cnst була б кращою.
Майкл

31

Або просто покладіть його на один рядок, як makeподобається:

if [ -a myApp ]; then rm myApp; fi;

це чудова відповідь у цьому випадку (і слід отримати оновлення), але не буде настільки добре, якщо дії були складнішими, і в цьому випадку просто перейдіть до використання \
Майкл

[-a myApp] && rm myApp
Робін Хсу

14

Відсутня крапка з комою

if [ -a myApp ];
then
  rm myApp
fi

Однак я припускаю, що ви перевіряєте наявність перед видаленням, щоб запобігти повідомленню про помилку. Якщо це так, ви можете просто скористатися, rm -f myAppяке "примушує" видалити, тобто не помилиться, якщо файл не існував.


1
Це саме те, що я хотів зробити. Дуже дякую.
Abruzzo Forte e Gentile

1
це не буде працювати в Makefile, тому що якщо if все ще поширюється на кілька рядків - вам потрібно або поставити це на одну лінію, або використовувати \ es. і навіть якщо ви додали \, ви все одно не вистачаєте деяких напівколонок.
Майкл

13
FILE1 = /usr/bin/perl
FILE2 = /nofile

ifeq ($(shell test -e $(FILE1) && echo -n yes),yes)
    RESULT1=$(FILE1) exists.
else
    RESULT1=$(FILE1) does not exist.
endif

ifeq ($(shell test -e $(FILE2) && echo -n yes),yes)
    RESULT2=$(FILE2) exists.
else
    RESULT2=$(FILE2) does not exist.
endif

all:
    @echo $(RESULT1)
    @echo $(RESULT2)

результати виконання:

bash> make
/usr/bin/perl exists.
/nofile does not exist.

Це мені допомогло, я думаю, дехто пропустив, що ОП запитували про makefile. Я не розумів, чому потрібен "&& echo -n так". Пояснення: якщо test -e поверне 1 (не знайдено), тоді оболонка не виконає команду echo, а отже, не буде відповідати "Так" в ifeq
Бред Дре

Я думаю, ви правильно це розумієте у своєму поясненні.
Робін Хсу

@BradDre - Змінює echo -n yesуспіх testв рядку yesбез NL. Потім ifeqможна порівняти його з жорстко закодованим yes. Все тому, що ifeqхоче, щоб рядок був порівняний, а не статус успіху команди оболонки.
Джессі

8

Рішення з однієї лінії:

   [ -f ./myfile ] && echo exists

Одне рядкове рішення з помилкою:

   [ -f ./myfile ] && echo exists || echo not exists

Приклад, використаний у моїх make cleanдирективах:

clean:
    @[ -f ./myfile ] && rm myfile || true

І make cleanзавжди працює без повідомлень про помилки!


3
просто зробіть @rm -f myfile. Через прапор "-f" "rm" вийде з 0, незалежно від того, існує файл чи ні.
Олексій Полонський

Або -rm myfileпровідна тире наказує зробити ігнорувати будь-яку помилку.
Джессі

1
У моєму випадку залишити || <помилка> викликала проблеми. Ваш остаточний приклад, коли ви повернули true, якщо файл не існував, вирішили це добре. Дякую!
Флік

Для мене шлях до мого файлу повинен бути з апострофом ('):@[ -f 'myfile' ] && rm myfile
Sten

0
test ! -f README.md || echo 'Support OpenSource!' >> README.md

"Якщо README.md не існує, не робіть нічого (і успішно вийдіть). В іншому випадку додайте текст до кінця."

Якщо ви використовуєте &&замість ||цього, ви створюєте помилку, коли файл не існує:

Makefile:42: recipe for target 'dostuff' failed
make: *** [dostuff] Error 1

0

Я намагався:

[ -f $(PROGRAM) ] && cp -f $(PROGRAM) $(INSTALLDIR)

І позитивний випадок спрацював, але мій оболонку bash ubuntu називає це ПРАВИЛЬНО і переривається на копію:

[ -f  ] && cp -f  /home/user/proto/../bin/
cp: missing destination file operand after '/home/user/proto/../bin/'

Після отримання цієї помилки я переглядаю, як перевірити, чи існує файл у make, і це відповідь ...


0
ifneq ("$(wildcard $(PATH_TO_FILE))","")
    FILE_EXISTS = 1
else
    FILE_EXISTS = 0
endif

Це рішення, розміщене вище, працює найкраще. Але переконайтеся, що ви не впорядковуєте порядок призначення PATH_TO_FILE Напр.,

PATH_TO_FILE = "/usr/local/lib/libhl++.a" # WILL NOT WORK

Це повинно бути

PATH_TO_FILE = /usr/local/lib/libhl++.a

-2

Відповіді, як відповідь від @ mark-wilkins, використовуючи \ для продовження рядків і; щоб припинити їх у оболонці або подібні до тих, що з @kenorb змінивши це на один рядок, добре і виправлять цю проблему.

є більш проста відповідь на початкову проблему (як вказував @ alexey-polonsky). Використовуйте прапор -f для rm, щоб він не викликав помилку

rm -f myApp

це простіше, швидше і надійніше. Тільки будьте обережні, щоб не закінчитися косою рисою та порожньою змінною

rm -f / $ (myAppPath) # НІКОЛИ РОБИТИ ЦЕ

ви можете врешті видалити систему.


1
Це хороша відповідь на видалення файлу, що був у ОП, але я впевнений, що більшість людей, які знайшли це питання, насправді не шукають видалення файлів; про це фактично свідчить той факт, що більшість відповідей взагалі навіть не згадують rm; До речі, я впевнений, що rm -f /$(myAppPath)шкоди теж не завдасть, тому що /це каталог, а його -rнемає.
cnst

Це не простіше рішення. Як сказав @cnst, можуть бути причини, чому оригінальний плакат не хоче просто робити rm -f, наприклад, вони можуть захотіти rmзмінну.
Dan Nguyen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.