ฉันจะตรวจสอบได้อย่างไรว่ามีไฟล์อยู่ใน Makefile เพื่อที่ฉันจะสามารถลบได้


143

ในส่วนสะอาดของฉัน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] 'ด้วยตัวมันเอง หากคุณได้รับข้อผิดพลาดนี้คุณอาจต้องการวิธีแก้ไขที่รวมบรรทัดเป็นหนึ่ง (โดยใช้) หรือลงท้ายด้วยแต่ละบรรทัดโดยไม่ขึ้นกับอีกบรรทัด ขณะนี้มีหลายสิ่งเหล่านี้ด้านล่าง
Michael

คำตอบ:


49

คำตอบอันดับสองกล่าวถึง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

ใช้งานไม่ได้จนกว่าฉันจะเพิ่มแบ็กสแลช `` หลังจากนั้นถ้า fi
Taras Matsyk

3
คำตอบนี้จะค่อนข้างแปลก การตรวจสอบไฟล์จะเกิดขึ้นเมื่อ makefile ถูกประมวลผล แต่การดำเนินการจะเกิดขึ้นในภายหลังเมื่อเป้าหมายถูกสร้างขึ้นโดย make หากคุณลบไฟล์ในระหว่างนี้ไฟล์นั้นจะไม่ถูกสร้างขึ้น ฉันได้ทำการแก้ไขเพื่อให้ชัดเจนขึ้น
Michael

ขอบคุณมาก! ประเด็นนี้ไม่ชัดเจนจากการอ่านคู่มือ
Kevin Buchs

211

เป็นเรื่องแปลกที่เห็นผู้คนจำนวนมากใช้เชลล์สคริปต์สำหรับสิ่งนี้ ฉันกำลังมองหาวิธีใช้ไวยากรณ์ 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 เอง เรียบร้อย :)
anoop

3
ช่วยให้เข้าใจสิ่งที่$(wildcard pattern)ทำจริง ลิงค์ดู
ดร. แดน

1
กระชับมากขึ้น:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0)
cmaster - คืนสถานะโมนิกา

3
เป็นที่น่าสังเกตว่าคุณกำลังใช้งาน Windows ภายใต้ cygwin การใช้สัญลักษณ์แทนในลักษณะนี้จะจับคู่ชื่อไฟล์ที่ตรงตามตัวพิมพ์เล็กและใหญ่ดังนั้นหากไฟล์มีอยู่ แต่มีตัวพิมพ์ต่างจากพา ธ ก็จะไม่พบ ดูเหมือนจะไม่มีวิธีใดที่จะลบล้างพฤติกรรมนี้ภายใน makefile ได้
Roger Sanders

62

ปัญหาคือเมื่อคุณแยกคำสั่งของคุณในหลายบรรทัด ดังนั้นคุณสามารถใช้\ท้ายบรรทัดเพื่อความต่อเนื่องตามด้านบนหรือคุณสามารถรับทุกอย่างในบรรทัดเดียวโดยใช้&&ตัวดำเนินการใน 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
ผมจะได้ upvoted แต่คุณไม่ได้เตือนว่าเป็นกรณีพิเศษสำหรับ-s exists and has a size greater than zeroคำถามตามที่เขียนคือขนาดไม่เชื่อเรื่องพระเจ้าดังนั้นควรตรวจสอบการมีอยู่โดยใช้test -eไฟล์หรือ-dไดเร็กทอรี ไฟล์ที่ว่างเปล่าจะมีประโยชน์โดยเฉพาะอย่างยิ่ง (เพื่อต้องการคำที่ดีกว่า) ตัวชี้วัด / makeยามรักษาการณ์ซึ่งอาจจะค่อนข้างที่เกี่ยวข้องกับ
underscore_d

ขอบคุณสำหรับคำแนะนำ เปลี่ยนเป็น-fค่าเริ่มต้นเนื่องจากเป็นเรื่องปกติที่จะใช้
kenorb

ฉันจะเข้าtestสู่ Windows ได้อย่างไร?
thowa

3
เพื่อให้สิ่งนี้ใช้งานได้คุณต้องเพิ่ม|| trueในตอนท้ายเพื่อให้คำสั่งกลับมาเป็นจริงเมื่อไม่มีไฟล์
jcubic

2
@AndrewMackenzie test -f myApp || CMDสังเกต||ดังนั้นถ้า-fจะล้มเหลว - ไม่มีอยู่ ( ||) จากนั้นรันคำสั่ง มันเข้าท่าไหม?
kenorb

50

อาจต้องใช้แบ็กสแลชที่ท้ายบรรทัดเพื่อความต่อเนื่อง (แม้ว่าอาจจะขึ้นอยู่กับรุ่นของยี่ห้อ):

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

2
ดูเหมือนจะไม่ใช่ไวยากรณ์ของ Makefile? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/...
Holms

@holms เป็นไวยากรณ์ทุบตี การหลีกเลี่ยงบรรทัดใหม่จะทำให้สามารถจัดการเป็นคำสั่ง bash เดียว ตามค่าเริ่มต้นในmakeบรรทัดใหม่จะเป็นคำสั่ง bash ใหม่ ข้อแม้ที่สำคัญของสิ่งนี้นอกเหนือจากความน่ารำคาญของการมีจำนวนมาก\ ในตอนท้ายของบรรทัดคือคำสั่งทุกคำสั่งจะต้องถูกยกเลิกด้วย;ซึ่งอาจมีนัยในการทุบตี
flungo

1
คำตอบที่ถูกต้องคือโดย @holms ทั้ง '\' หรือสัญลักษณ์แทนไม่ได้มีไว้สำหรับจุดประสงค์นี้อย่างแน่นอน เหตุผลที่คุณต้องใช้สัญลักษณ์แทนคือเพื่อความชัดเจนของรหัส วิธีนี้ไม่เพียง แต่อ่านได้น้อยลงเท่านั้น แต่ยังมีแนวโน้มที่จะเกิดข้อผิดพลาดทางไวยากรณ์
Maitreya

1
ลิงก์ @holms ที่ให้มาใช้ไม่ได้อีกต่อไปให้ใช้gnu.org/software/make/manual/make.html#Conditionalsแทน
fero

นี่เป็นคำตอบที่ดีเพราะตรงกับสิ่งที่ผิดกับสิ่งที่ผู้ถามเดิมทำและมันจะทำงานได้ขึ้นอยู่กับว่ามีไฟล์อยู่ในเวลาที่สร้างเป้าหมายหรือไม่ในเวลาที่ Makefile เริ่มทำงานซึ่งเป็นสิ่งที่คนส่วนใหญ่คาดหวัง และต้องการเวลาส่วนใหญ่ ในบางกรณีคำตอบจาก @cnst จะดีกว่า
Michael

34

หรือใส่ไว้ในบรรทัดเดียวก็ได้ตามmakeชอบ:

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

นั่นเป็นคำตอบที่ดีในกรณีนี้ (และควรได้รับการโหวตเพิ่มขึ้น) แต่จะไม่ได้ผลดีนักหากการกระทำนั้นซับซ้อนกว่านี้ในกรณีนี้ให้เปลี่ยนไปใช้ \
Michael

[-a myApp] && rm myApp
Robin Hsu

14

ไม่มีเครื่องหมายอัฒภาค

if [ -a myApp ];
then
  rm myApp
fi

อย่างไรก็ตามฉันถือว่าคุณกำลังตรวจสอบการมีอยู่ก่อนที่จะลบเพื่อป้องกันข้อความแสดงข้อผิดพลาด ถ้าเป็นเช่นนั้นคุณสามารถใช้การrm -f myAppลบที่ "บังคับ" กล่าวคือจะไม่เกิดข้อผิดพลาดหากไม่มีไฟล์


1
นั่นคือสิ่งที่ฉันอยากทำ ขอบคุณมาก.
Abruzzo Forte e Gentile

1
สิ่งนี้จะใช้ไม่ได้ใน Makefile เนื่องจาก if ยังคงกระจายอยู่ในหลายบรรทัดคุณต้องวางไว้ในบรรทัดเดียวหรือใช้ \ es และแม้ว่าคุณจะเพิ่มสิ่งนี้เข้าไปคุณก็ยังไม่มีเครื่องหมายกึ่งโคลอนอยู่
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)

ผลการดำเนินการ:

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

สิ่งนี้ช่วยฉันได้ฉันคิดว่ามีบางคนพลาดที่ OP กำลังถามเกี่ยวกับ makefile ฉันไม่เข้าใจว่าทำไม "&& echo -n yes" จึงจำเป็น คำอธิบาย: หาก test -e ส่งคืน 1 (ไม่พบ) เชลล์จะไม่ดำเนินการคำสั่ง echo ดังนั้นจึงไม่ตรงกับ yes ใน ifeq
Brad Dre

ฉันคิดว่าคุณเข้าใจถูกต้องในคำชี้แจงของคุณ
Robin Hsu

@BradDre - การecho -n yesเปลี่ยนแปลงความสำเร็จของtestสตริงyesโดยไม่มี NL แล้วสามารถเปรียบเทียบกับรหัสยากifeq yesทั้งหมดเป็นเพราะifeqต้องการให้สตริงเปรียบเทียบไม่ใช่สถานะความสำเร็จจากคำสั่งเชลล์
Jesse Chisholm

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 ไม่ว่าไฟล์นั้นจะมีอยู่หรือไม่ก็ตาม
Alexey Polonsky

หรือ-rm myfileเส้นประลูกค้าเป้าหมายบอกให้ละเว้นข้อผิดพลาดใด ๆ
Jesse Chisholm

1
ในกรณีของฉันการออกจาก || <error action> ทำให้เกิดปัญหา ตัวอย่างสุดท้ายของคุณที่คุณส่งคืนจริงหากไฟล์ไม่มีอยู่จะได้กล่าวถึงสิ่งนี้อย่างดี ขอบคุณ!
Flic

สำหรับฉันเส้นทางไปยัง myfile ต้องอยู่กับเครื่องหมายวรรคตอน ('):@[ -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)

และกรณีที่เป็นบวกใช้งานได้ แต่ Ubuntu bash shell ของฉันเรียกสิ่งนี้ว่า TRUE และแตกบนสำเนา:

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

หลังจากได้รับข้อผิดพลาดนี้ฉัน google จะตรวจสอบว่ามีไฟล์อยู่ใน 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เลย BTW ฉันค่อนข้างมั่นใจว่าrm -f /$(myAppPath)จะไม่สร้างความเสียหายใด ๆ เช่นกันเพราะ/เป็นไดเรกทอรีและ-rไม่มี
cnst

นี่ไม่ใช่วิธีแก้ปัญหาที่ง่ายกว่านี้ เช่นเดียวกับที่ @cnst กล่าวไว้อาจมีเหตุผลว่าทำไมผู้โพสต์ต้นฉบับไม่เพียงต้องการทำrm -fเช่นพวกเขาอาจต้องการrmตัวแปร
Dan Nguyen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.