Làm cách nào để kiểm tra xem tập tin có tồn tại trong Makefile để tôi có thể xóa nó không?


135

Trong phần sạch của tôi, Makefiletôi đang cố kiểm tra xem tập tin có tồn tại trước khi xóa vĩnh viễn không. Tôi sử dụng mã này nhưng tôi nhận được lỗi.

Có gì sai với nó?

 if [ -a myApp ]
 then
     rm myApp
 fi

Tôi nhận được thông báo lỗi này

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

MyApp là một biến hoặc một tên tệp thực tế?
Bug Downloader

myApp dành cho myApplication, tức là tên tệp theo quy trình xây dựng.
Abruzzo Forte e Gentile

5
Nếu bạn chỉ muốn tránh thực hiện dừng nếu tập tin không tồn tại, rm -rf myAppcó thể là một sự thay thế. Hoặc trước lệnh có dấu gạch ngang ( -rm myApp) để bỏ qua lỗi từ rm (tuy nhiên nó sẽ in một thông báo xấu).
thomasa88

1
Vấn đề của bạn là làm cho mỗi dòng xử lý theo một quy tắc như một lệnh riêng biệt và gửi chúng riêng lẻ vào trình bao. Nó giống như chỉ chạy `if [-a myApp] '. Nếu bạn gặp lỗi này, bạn cần một giải pháp nối các dòng thành một (sử dụng) hoặc kết thúc với mỗi dòng độc lập với dòng khác. Bây giờ có một vài trong số này dưới đây.
Michael

Câu trả lời:


40

ifeqTuy nhiên, câu trả lời hàng đầu thứ hai đề cập đến việc không đề cập đến việc những cái này phải ở cùng cấp độ với tên của mục tiêu, ví dụ, để tải xuống một tệp chỉ khi nó không tồn tại, có thể sử dụng mã sau:

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

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

đã không hoạt động cho đến khi tôi thêm dấu gạch chéo ngược `` sau if fi
Taras Matsyk

3
Câu trả lời này sẽ hơi kỳ lạ; kiểm tra tệp xảy ra khi tệp thực hiện được xử lý nhưng hành động sẽ xảy ra sau đó khi mục tiêu được tạo bởi make. Nếu bạn xóa tệp trong thời gian đó thì tệp sẽ không được tạo. Tôi đã đưa vào một chỉnh sửa để làm cho nó rõ ràng hơn.
Michael

Cảm ơn nhiều! Điểm này không rõ ràng từ việc đọc hướng dẫn.
Kevin Buchs

207

Thật kỳ lạ khi thấy rất nhiều người sử dụng shell scripting cho việc này. Tôi đang tìm cách sử dụng cú pháp makefile riêng, bởi vì tôi đang viết cái này bên ngoài bất kỳ mục tiêu nào. Bạn có thể sử dụng wildcardchức năng để kiểm tra xem tập tin có tồn tại không:

 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 

Cập nhật:

Tôi tìm thấy một cách thực sự hiệu quả với tôi:

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

4
đã thử điều này, nhưng tôi chỉ tiếp tục nhận đượcMakefile:133: *** unterminated call to function `wildcard': missing `)'. Stop.
Ant6n

1
Sử dụng tuyệt vời của ký tự đại diện, vì vậy nó có thể được thực hiện với chính makefile. Không gọn gàng :)
anoop

3
Giúp để hiểu những gì $(wildcard pattern)thực sự làm. Xem liên kết .
Bác sĩ Dan

1
Súc tích hơn:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - phục hồi monica

2
Điều đáng chú ý là nếu bạn đang chạy trên Windows theo cygwin, sử dụng ký tự đại diện theo cách này sẽ khớp với trường hợp nhạy cảm với tên tệp, vì vậy nếu tệp tồn tại nhưng với trường hợp khác với đường dẫn thì nó sẽ không được tìm thấy. Dường như không có cách nào để ghi đè hành vi này trong tệp thực hiện.
Roger Sanders

59

Vấn đề là khi bạn chia lệnh của bạn trên nhiều dòng. Vì vậy, bạn có thể sử dụng \ở cuối dòng để tiếp tục như trên hoặc bạn có thể nhận mọi thứ trên một dòng với &&toán tử trong bash.

Sau đó, bạn có thể sử dụng testlệnh để kiểm tra xem tệp có tồn tại không, ví dụ:

test -f myApp && echo File does exist

-f file Đúng nếu tập tin tồn tại và là một tập tin thông thường.

-s file Đúng nếu tệp tồn tại và có kích thước lớn hơn 0.

hoặc không:

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

Điều testnày tương đương với [lệnh.

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

và nó sẽ hoạt động như trong ví dụ ban đầu của bạn.

Xem: help [hoặc help testđể biết thêm cú pháp.


4
Tôi sẽ nâng cấp, nhưng bạn đã không cảnh báo đó -slà trường hợp đặc biệt exists and has a size greater than zero. Câu hỏi như được viết là không xác định kích thước, vì vậy cần kiểm tra sự tồn tại bằng cách sử dụng test -echo một tệp hoặc -dcho một thư mục. Các tệp trống có thể đặc biệt hữu ích vì (vì muốn có thuật ngữ tốt hơn) các chỉ số / trọng điểm, có thể khá phù hợp make.
gạch dưới

Cám ơn vì sự gợi ý. Thay đổi -ftheo mặc định, vì nó phổ biến hơn để sử dụng.
kenorb

Làm thế nào tôi có thể nhận được testtrên Windows?
thowa 4/2/2016

3
Để làm việc này, bạn cần thêm || truevào cuối để lệnh trả về đúng khi tệp không tồn tại.
jcubic

2
@AndrewMackenzie test -f myApp || CMD, hãy chú ý ||, vì vậy nếu -fthất bại - không tồn tại ( ||), thì hãy chạy lệnh. Liệu nó có ý nghĩa?
kenorb

50

Nó có thể cần một dấu gạch chéo ngược ở cuối dòng để tiếp tục (mặc dù có lẽ điều đó phụ thuộc vào phiên bản tạo):

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

2
dường như không phải là một cú pháp Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_ch CHƯƠNG / từ
holms 13/12/13

@holms nó là một cú pháp bash. Bằng cách thoát khỏi các dòng mới, nó cho phép nó được xử lý như một lệnh bash duy nhất. Theo mặc định trong makemột dòng mới sẽ là một lệnh bash mới. Nhắc nhở chính của điều này, ngoại trừ sự khó chịu khi có nhiều dòng \ ở cuối dòng là mọi lệnh phải được chấm dứt với ;điều mà nếu không sẽ được ẩn trong bash.
flungo

1
Câu trả lời đúng là bởi @holms. Cả '\' hay ký tự đại diện đều không được dùng cho mục đích này. Lý do tại sao bạn sẽ sử dụng ký tự đại diện là để rõ ràng về mã. Phương pháp này không chỉ dễ đọc hơn mà còn dễ bị lỗi cú pháp hơn.
Di Lặc

1
liên kết @holms được cung cấp không hoạt động nữa, hãy sử dụng gnu.org/software/make/manual/make.html#Cond điều kiện thay thế
fero

Đây là một câu trả lời tuyệt vời vì nó phù hợp với những gì sai với những gì người hỏi ban đầu đã làm và nó sẽ hoạt động tùy thuộc vào việc tệp tồn tại vào thời điểm mục tiêu được xây dựng chứ không phải là lúc Makefile được bắt đầu, đó là điều mà hầu hết mọi người mong đợi và muốn hầu hết thời gian. Trong một vài trường hợp kỳ lạ, câu trả lời từ @cnst sẽ tốt hơn.
Michael

30

Hoặc chỉ cần đặt nó trên một dòng, như makenó thích:

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

đó là một câu trả lời tuyệt vời trong trường hợp này (và sẽ nhận được sự ủng hộ) nhưng sẽ không hoạt động tốt nếu các hành động phức tạp hơn, trong trường hợp đó chỉ cần chuyển sang sử dụng \
Michael

[-a myApp] && rm myApp
Robin Hsu

14

Thiếu một dấu chấm phẩy

if [ -a myApp ];
then
  rm myApp
fi

Tuy nhiên, tôi giả sử bạn đang kiểm tra sự tồn tại trước khi xóa để ngăn thông báo lỗi. Nếu vậy, bạn chỉ có thể sử dụng rm -f myApp"lực lượng" xóa, nghĩa là không có lỗi nếu tệp không tồn tại.


1
Đó chính xác là những gì tôi muốn làm. Cảm ơn rất nhiều.
Abruzzo Forte e Gentile

1
điều này sẽ không hoạt động trong Makefile bởi vì if vẫn trải đều trên nhiều dòng - bạn cần đặt cái này trên một dòng hoặc sử dụng \ es. và ngay cả khi bạn đã thêm \ es, bạn vẫn còn thiếu một số dấu chấm phẩy.
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)

kết quả thực hiện:

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

Điều này đã giúp tôi, tôi nghĩ rằng một số người đã bỏ lỡ rằng OP đã hỏi về makefile. Tôi không hiểu tại sao "&& echo -n yes" là cần thiết. Giải thích: nếu test -e trả về 1 (không tìm thấy) thì shell sẽ không thực thi lệnh echo và do đó sẽ không khớp với yes trong ifeq
Brad Dre

Tôi nghĩ rằng bạn hiểu nó chính xác trong tuyên bố giải thích của bạn.
Robin Hsu

@BradDre - Sự echo -n yesthay đổi thành công của testchuỗi yeskhông có NL. Sau ifeqđó có thể so sánh nó với mã hóa cứng yes. Tất cả chỉ vì ifeqmuốn một chuỗi so sánh, không phải là trạng thái thành công từ lệnh shell.
Jesse Chisholm

8

Giải pháp một dòng:

   [ -f ./myfile ] && echo exists

Giải pháp một dòng với hành động lỗi:

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

Ví dụ được sử dụng trong các make cleanchỉ thị của tôi :

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

make cleanluôn luôn hoạt động mà không có bất kỳ thông báo lỗi!


3
chỉ cần làm @rm -f myfile. Do cờ "-f", "rm" sẽ thoát với 0 bất kể tệp có tồn tại hay không.
Alexey Polonsky

Hoặc, -rm myfiledấu gạch ngang dẫn làm cho bỏ qua bất kỳ lỗi nào.
Jesse Chisholm

1
Trong trường hợp của tôi, rời khỏi | | <hành động lỗi> gây ra sự cố. Ví dụ cuối cùng của bạn, nơi bạn trả về đúng nếu tệp không tồn tại, đã xử lý vấn đề này một cách độc đáo. Cảm ơn bạn!
Flic

Đối với tôi, con đường đến với myfile cần phải có dấu nháy đơn ('):@[ -f 'myfile' ] && rm myfile
Sten

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

"Nếu README.md không tồn tại, không làm gì cả (và thoát thành công). Nếu không, hãy thêm văn bản vào cuối."

Nếu bạn sử dụng &&thay vì ||sau đó bạn sẽ phát sinh lỗi khi tệp không tồn tại:

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

0

Tôi đã cố gắng:

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

Và trường hợp tích cực đã hoạt động nhưng shell bash Ubuntu của tôi gọi đây là TRUE và phá vỡ bản sao:

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

Sau khi gặp lỗi này, tôi google cách kiểm tra xem một tệp có tồn tại hay không và đây là câu trả lời ...


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

Giải pháp này được đăng ở trên hoạt động tốt nhất. Nhưng hãy chắc chắn rằng bạn không xâu chuỗi bài tập PATH_TO_FILE Ví dụ:

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

Nó phải là

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

-2

Các câu trả lời giống như câu trả lời từ @ mark-wilkins bằng cách sử dụng \ để tiếp tục dòng và; để chấm dứt chúng trong shell hoặc giống như những cái từ @kenorb thay đổi dòng này thành một dòng là tốt và sẽ khắc phục vấn đề này.

có một câu trả lời đơn giản hơn cho vấn đề ban đầu (như @ alexey-polonsky đã chỉ ra). Sử dụng cờ -f để rm để nó không gây ra lỗi

rm -f myApp

Điều này đơn giản hơn, nhanh hơn và đáng tin cậy hơn. Chỉ cần cẩn thận để không kết thúc bằng một dấu gạch chéo và một biến trống

rm -f / $ (myAppPath) #NEVER LÀM ĐIỀU NÀY

bạn có thể sẽ xóa hệ thống của bạn.


1
Đây là một câu trả lời tốt để xóa tệp mà OP có, nhưng tôi khá chắc chắn rằng hầu hết những người tìm thấy câu hỏi này không thực sự tìm cách xóa bất kỳ tệp nào; điều này thực sự được chứng minh bằng thực tế là hầu hết các câu trả lời thậm chí không đề cập đến rm; BTW, tôi khá chắc chắn rằng rm -f /$(myAppPath)sẽ không gây ra bất kỳ thiệt hại nào, bởi vì /là một thư mục, và -rbị thiếu.
cnst

Đây không phải là một giải pháp đơn giản hơn. Giống như @cnst đã nói, có thể có lý do tại sao người đăng ban đầu không chỉ đơn giản muốn làm một rm -f, ví dụ họ có thể muốn rmmột biến.
Dân Nguyễn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.