Jak sprawdzić, czy plik istnieje w Makefile, abym mógł go usunąć?


135

W czystej sekcji mojego Makefilepróbuję sprawdzić, czy plik istnieje przed trwałym usunięciem. Używam tego kodu, ale otrzymuję błędy.

Co jest z tym nie tak?

 if [ -a myApp ]
 then
     rm myApp
 fi

Otrzymuję ten komunikat o błędzie

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

Czy myApp jest zmienną czy rzeczywistą nazwą pliku?
BugFinder

myApp jest dla myApplication, tj. nazwa pliku w procesie budowania.
Abruzzo Forte e Gentile

5
Jeśli chcesz po prostu uniknąć zatrzymywania się, jeśli plik nie istnieje, rm -rf myAppmoże być alternatywą. Lub poprzedzając polecenie myślnikiem ( -rm myApp), aby make zignorował błąd z rm (wypisze jednak brzydki komunikat).
thomasa88,

1
Twój problem polegał na tym, że make traktuje każdą linię w regule jako osobne polecenie i wysyła je indywidualnie do powłoki. To tak, jakby działać samodzielnie `if [-a myApp] '. Jeśli pojawi się ten błąd, albo potrzebujesz rozwiązania, które łączy linie w jedną (używając) lub które kończy się każdą linią niezależnie od drugiej. Poniżej znajduje się kilka z nich.
Michael

Odpowiedzi:


40

Druga najczęstsza odpowiedź wspomina ifeqjednak, że nie wspomina, że ​​muszą one znajdować się na tym samym poziomie, co nazwa celu, np. Aby pobrać plik tylko wtedy, gdy obecnie nie istnieje, można użyć następującego kodu:

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

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

nie działało, dopóki nie dodałem odwrotnego ukośnika `` after if fi
Taras Matsyk

3
Ta odpowiedź będzie trochę dziwna; sprawdzanie pliku ma miejsce podczas przetwarzania makefile, ale akcja nastąpi później, gdy cel zostanie zbudowany przez make. Jeśli w międzyczasie usuniesz plik, plik nie zostanie utworzony. Wprowadziłem edycję, aby była jaśniejsza.
Michael

Dzięki wielkie! Ta kwestia nie była jasna po przeczytaniu instrukcji.
Kevin Buchs

207

Dziwnie jest widzieć tak wielu ludzi używających do tego skryptów powłoki. Szukałem sposobu na użycie natywnej składni makefile, ponieważ piszę to poza jakimkolwiek celem. Możesz użyć wildcardfunkcji, aby sprawdzić, czy plik istnieje:

 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 

Aktualizacja:

Znalazłem sposób, który naprawdę mi odpowiada:

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

4
próbowałem tego, ale po prostu wciąż dostajęMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n,

1
Świetne użycie symbolu wieloznacznego, więc można to zrobić za pomocą samego pliku makefile.
Schludnie

3
Pomaga również zrozumieć, co $(wildcard pattern)tak naprawdę robi. Zobacz link .
Dr Dan

1
Bardziej zwięźle:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster

2
Warto zauważyć, że jeśli używasz systemu Windows pod cygwin, użycie symbolu wieloznacznego w ten sposób powoduje rozróżnienie wielkości liter w nazwie pliku, więc jeśli plik istnieje, ale ma inną wielkość liter niż ścieżka, nie zostanie znaleziony. Wygląda na to, że nie ma sposobu na obejście tego zachowania w pliku makefile.
Roger Sanders

59

Problem występuje, gdy podzielisz polecenie na wiele linii. Możesz więc użyć znaku \na końcu linii do kontynuacji, jak powyżej, lub możesz uzyskać wszystko w jednej linii za pomocą &&operatora w bash.

Następnie możesz użyć testpolecenia, aby sprawdzić, czy plik istnieje, np .:

test -f myApp && echo File does exist

-f file Prawda, jeśli plik istnieje i jest zwykłym plikiem.

-s file Prawda, jeśli plik istnieje i ma rozmiar większy niż zero.

lub nie:

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

testJest równoważne [poleceniu.

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

i będzie działać tak, jak w oryginalnym przykładzie.

Zobacz: help [lub help testdla dalszej składni.


4
Głosowałbym za, ale nie ostrzegałeś, że -sjest to szczególny przypadek exists and has a size greater than zero. Pytanie, tak jak zostało napisane, jest niezależne od rozmiaru, więc istnienie powinno być sprawdzane przy użyciu test -epliku lub -dkatalogu. Puste pliki mogą być szczególnie przydatne jako (z braku lepszego terminu) wskaźniki / wskaźniki, które mogą być całkiem istotne dla make.
underscore_d

Dzieki za sugestie. Zmieniony -fdomyślnie, ponieważ jest bardziej powszechny w użyciu.
kenorb

Jak mogę uzyskać dostęp testdo systemu Windows?
thowa

3
Aby to zadziałało, musisz dodać || truena końcu, aby polecenie zwracało wartość true, gdy plik nie istnieje.
jcubic

2
@AndrewMackenzie test -f myApp || CMD, zwróć uwagę na ||, więc jeśli -fnie powiedzie się - nie istnieje ( ||), uruchom polecenie. Czy jest sens?
kenorb

50

Kontynuacja może wymagać odwrotnego ukośnika na końcu linii (chociaż może to zależy od wersji make):

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

2
wygląda na to, że nie jest składnią Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/…
holms

@holms to składnia bash. Dzięki ucieczce z nowych wierszy może być obsługiwane jako pojedyncze polecenie bash. Domyślnie w makenowej linii będzie nowe polecenie bash. Głównym zastrzeżeniem, innym niż uciążliwość wynikająca z posiadania wielu znaków \ na końcu linii, jest to, że każde polecenie musi być zakończone poleceniem, ;które w przeciwnym razie byłoby ukryte w bash.
flungo

1
Prawidłowa odpowiedź to @holms. Ani znak „\”, ani symbol wieloznaczny nie są dokładnie przeznaczone do tego celu. Powodem, dla którego warto użyć symbolu wieloznacznego, jest przejrzystość kodu. Ta metoda jest nie tylko mniej czytelna, ale także bardziej podatna na błędy składniowe.
Maitreya

1
podany link @holms już nie działa, użyj zamiast tego gnu.org/software/make/manual/make.html#Conditionals
fero

to jest świetna odpowiedź, ponieważ pasuje do tego, co jest nie tak z tym, co zrobił pierwotny pytający i będzie działać w zależności od tego, czy plik istnieje w momencie budowania celu, a nie w momencie uruchamiania Makefile, czego większość ludzi spodziewałaby się i chcę przez większość czasu. W kilku dziwnych przypadkach odpowiedź z @cnst byłaby lepsza.
Michael

30

Lub po prostu umieść to w jednej linii, tak jak makelubi:

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

to świetna odpowiedź w tym przypadku (i powinna uzyskać pozytywne głosy), ale nie będzie działać tak dobrze, jeśli działania były bardziej złożone, w takim przypadku po prostu przełącz się na używanie \
Michael

[-a myApp] && rm myApp
Robin Hsu

14

Brak średnika

if [ -a myApp ];
then
  rm myApp
fi

Zakładam jednak, że przed usunięciem sprawdzasz istnienie, aby zapobiec wyświetleniu komunikatu o błędzie. Jeśli tak, możesz po prostu użyć polecenia rm -f myApp„wymusza” usunięcie, tj. Nie wyświetla błędu, jeśli plik nie istnieje.


1
Właśnie to chciałem zrobić. Wielkie dzięki.
Abruzzo Forte e Gentile

1
to nie zadziała w Makefile, ponieważ if jest nadal rozłożone na wiele linii - musisz albo umieścić to w jednej linii, albo użyć \ es. a nawet jeśli dodałeś \ es, nadal brakuje kilku średników.
Michael

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)

wyniki realizacji:

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

Pomogło mi to, myślę, że niektórzy przegapili, że OP pytał o makefile. Nie rozumiem, dlaczego „&& echo -n yes” jest konieczne. Wyjaśnienie: jeśli test -e zwróci 1 (nie znaleziono), to powłoka nie wykona polecenia echo i dlatego nie dopasuje tak w ifeq
Brad Dre

Myślę, że rozumiesz to poprawnie w swoim wyjaśnieniu.
Robin Hsu,

@BradDre - echo -n yesZmienia sukces testna ciąg yesbez NL. Następnie ifeqmożna porównać to z zakodowanym na stałe yes. Wszystko dlatego, że ifeqchce porównać ciąg znaków, a nie stan sukcesu polecenia powłoki.
Jesse Chisholm

8

Rozwiązanie jednoprzewodowe:

   [ -f ./myfile ] && echo exists

Rozwiązanie jednokreskowe z działaniem błędu:

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

Przykład użyty w moich make cleandyrektywach:

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

I make cleanzawsze działa bez żadnych komunikatów o błędach!


3
po prostu zrób @rm -f myfile. Ze względu na flagę „-f”, „rm” zakończy działanie z wartością 0, niezależnie od tego, czy plik istnieje, czy nie.
Alexey Polonsky

Lub -rm myfiległówny myślnik mówiący make, aby zignorować każdy błąd.
Jesse Chisholm

1
W moim przypadku pomijając || <działanie błędu> spowodowało problemy. Twój ostatni przykład, w którym zwracałeś prawdę, jeśli plik nie istnieje, ładnie to rozwiązał. Dziękuję Ci!
Flic

Dla mnie ścieżka do myfile musi być z apostrofem ('):@[ -f 'myfile' ] && rm myfile
Sten

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

„Jeśli plik README.md nie istnieje, nic nie rób (i zakończ pomyślnie). W przeciwnym razie dołącz tekst na końcu”.

Jeśli użyjesz &&zamiast ||tego, wygenerujesz błąd, gdy plik nie istnieje:

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

0

Próbowałem:

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

I pozytywny przypadek zadziałał, ale moja powłoka ubuntu bash nazywa to PRAWDA i psuje kopię:

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

Po otrzymaniu tego błędu wyszukuję w Google, jak sprawdzić, czy plik istnieje w make i to jest odpowiedź ...


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

To rozwiązanie zamieszczone powyżej działa najlepiej. Ale upewnij się, że nie określasz przypisania PATH_TO_FILE, np.

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

To musi być

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

-2

Odpowiedzi takie jak ta z @ mark-wilkins używająca \ do kontynuowania linii i; zakończenie ich w powłoce lub jak te z @kenorb zmiana tego na jedną linię są dobre i naprawią ten problem.

istnieje prostsza odpowiedź na pierwotny problem (jak wskazał @ alexey-polonsky). Użyj flagi -f do rm, aby nie wywoływała błędu

rm -f myApp

jest to prostsze, szybsze i bardziej niezawodne. Uważaj tylko, aby nie zakończyć się ukośnikiem i pustą zmienną

rm -f / $ (myAppPath) # NIGDY TEGO NIE ROBIĆ

możesz skończyć usunięciem systemu.


1
To jest dobra odpowiedź do usunięcia pliku, który miał OP, ale jestem prawie pewien, że większość ludzi, którzy znajdą to pytanie, tak naprawdę nie szuka usunięcia żadnych plików; w rzeczywistości dowodzi tego fakt, że większość odpowiedzi w ogóle nie wspomina rm; BTW, jestem prawie pewien, że rm -f /$(myAppPath)to też nie spowoduje żadnych szkód, ponieważ /jest to katalog, którego -rbrakuje.
cnst

To nie jest prostsze rozwiązanie. Jak powiedział @cnst, mogą istnieć powody, dla których oryginalny plakat nie chce po prostu zrobić czegoś rm -f, np. Może chcieć rmużyć zmiennej.
Dan Nguyen
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.