Как мне проверить, существует ли файл в 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 05
myApp для myApplication, то есть имя файла в процессе сборки.
Абруццо Форте и Джентиле
5
Если вы просто хотите избежать остановки make, если файл не существует, это rm -rf myAppможет быть альтернативой. Или, предшествуя команде с помощью dash ( -rm myApp), make заставит игнорировать ошибку от rm (однако она выведет некрасивое сообщение).
thomasa88
1
Ваша проблема заключалась в том, что make рассматривает каждую строку в правиле как отдельную команду и отправляет их по отдельности в оболочку. Это все равно что запускать просто `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
CNST
источник
не работало, пока я не добавил обратную косую черту `` после if fi
Тарас Мацык
3
Этот ответ будет немного странным; проверка файла происходит при обработке make-файла, но действие произойдет позже, когда цель будет собрана 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
Holms
источник
4
попробовал это, но я просто продолжаю получатьMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n
1
Отличное использование подстановочных знаков, так что это можно сделать с помощью самого make-файла. Аккуратные :)
Anoop
3
Помогает также понять, что на $(wildcard pattern)самом деле делает. См. Ссылку .
Доктор Дэн
1
Более кратко:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - восстановить монику
2
Стоит отметить, что если вы работаете в Windows под Cygwin, использование подстановочного знака таким образом соответствует регистрозависимому имени файла, поэтому, если файл существует, но с регистром, отличным от пути, он не будет найден. Кажется, нет никакого способа изменить это поведение в make-файле.
Роджер Сандерс
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для дальнейшего синтаксиса.

kenorb
источник
4
Я бы проголосовал, но вы не предупредили, что -sэто особый случай для exists and has a size greater than zero. Вопрос в том виде, в котором он написан, не зависит от размера, поэтому наличие должно быть проверено с test -eпомощью файла или -dкаталога. Пустые файлы могут быть особенно полезны в качестве (если не сказать лучшего термина) индикаторов / стражей, которые могут быть весьма актуальны для make.
underscore_d
Спасибо за предложение. Изменено -fпо умолчанию, так как используется чаще.
Кенорб
Как я могу получить testна Windows?
thowa
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. Главное предостережение этого, помимо раздражения, связанного с наличием большого количества \ символов в конце строк, заключается в том, что каждая команда должна заканчиваться символом, ;который в противном случае был бы неявным в bash.
flungo
1
Правильный ответ - @holms. Ни '\', ни подстановочный знак не предназначены для этой цели. Причина, по которой вы должны использовать подстановочные знаки, - это ясность кода. Этот метод не только менее читабелен, но и более подвержен синтаксическим ошибкам.
Майтрейя
1
то ссылка @holms предоставляемые больше не работает, использовать gnu.org/software/make/manual/make.html#Conditionals вместо
Феро
это отличный ответ, потому что он соответствует тому, что не так, с тем, что сделал исходный вопросник, и он будет работать в зависимости от того, существует ли файл во время сборки цели, а не во время запуска Makefile, чего ожидает большинство людей и хочу большую часть времени. В некоторых странных случаях ответ от @cnst будет лучше.
Майкл
32

Или просто поместите в одну строчку, как makeнравится:

if [ -a myApp ]; then rm myApp; fi;
Йерун
источник
это отличный ответ в данном случае (и он должен получить положительные голоса), но он не будет работать так хорошо, если действия будут более сложными, и в этом случае просто переключитесь на использование \
Майкл
[-a myApp] && rm myApp
Робин Хсу
14

Отсутствует точка с запятой

if [ -a myApp ];
then
  rm myApp
fi

Однако я предполагаю, что вы проверяете существование перед удалением, чтобы предотвратить появление сообщения об ошибке. Если это так, вы можете просто использовать те, rm -f myAppкоторые «принудительно» удаляют, т.е. не выдают ошибку, если файл не существует.

drysdam
источник
1
Это именно то, что я хотел сделать. Большое спасибо.
Abruzzo Forte e Gentile
1
это не будет работать в Makefile, потому что if по-прежнему распространяется на несколько строк - вам нужно либо поместить его в одну строку, либо использовать \ es. и даже если вы добавили \ 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.
Робин Хсу
источник
Это помогло мне, я думаю, что некоторые упустили, что OP спрашивал о makefile. Я не понял, почему "&& echo -n yes" необходимо. Объяснение: если test -e возвращает 1 (не найдено), оболочка не выполнит команду echo и, следовательно, не совпадет с yes в ifeq
Brad Dre
Я думаю, вы правильно поняли это в своем объяснительном заявлении.
Робин Сюй,
@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, если файл не существовал, хорошо сработал. Спасибо!
Flic
Для меня путь к моему файлу должен быть с апострофом ('):@[ -f 'myfile' ] && rm myfile
Стен
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)

И положительный случай сработал, но моя оболочка ubuntu bash называет это ИСТИННЫМ и прерывает копию:

[ -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
Это хороший ответ для удаления файла, который был у OP, но я почти уверен, что большинство людей, которые задаются этим вопросом, на самом деле не ищут удаления каких-либо файлов; на самом деле об этом свидетельствует тот факт, что в большинстве ответов даже не упоминается rm; Кстати, я почти уверен, что это rm -f /$(myAppPath)тоже не повредит, потому что /это каталог, а его -rнет.
cnst
Это не простое решение. Как сказал @cnst, могут быть причины, по которым исходный постер просто не хочет делать что-то rm -f, например, они могут захотеть rmпеременную.
Дэн Нгуен