如何检查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 2011年

myApp用于myApplication,即构建过程中的文件名。
Abruzzo Forte e Gentile

5
如果您只是想避免在文件不存在的情况下停止运行,则rm -rf myApp可以选择替代方法。或在命令前加上破折号(-rm myApp),以使make忽略rm的错误(但是,它将显示一条难看的消息)。
thomasa88

1
您的问题是,make将规则中的每一行都视为一个单独的命令,并将其分别发送到外壳。这就好比单独运行`if [-a myApp]。如果出现此错误,则需要一种解决方案,将各行连接成一个(使用),或者以每行独立于另一行结束。现在下面有几个。
迈克尔

Answers:


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

没有工作,直到我说反斜线``如果连接后
塔拉斯Matsyk

3
这个答案有点怪异。文件检查是在处理makefile时发生的,但是稍后将在make生成目标时执行该操作。如果您同时删除该文件,则不会创建该文件。我进行了修改以使其更加清晰。
迈克尔

非常感谢!从阅读手册中并不清楚这一点。
凯文·布克斯

207

看到如此多的人为此使用shell脚本,这很奇怪。我一直在寻找一种使用本机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 2014年

1
大量使用通配符,因此可以使用makefile本身来完成。整洁:)
Anoop

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 如果文件存在且为常规文件,则为True。

-s file 如果文件存在且大小大于零,则为True。

或不:

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
underscore_d

谢谢你的建议。-f默认更改,因为它更常用。
kenorb

如何test在Windows上安装?
thowa '16

3
为了|| true使它起作用,您需要在末尾添加,以便在文件不存在时命令返回true。
jcubic

2
@AndrewMackenzie test -f myApp || CMD,请注意||,因此,如果-f失败-不存在(||),请运行命令。是否有意义?
kenorb

50

在行的末尾可能需要反斜杠以继续(尽管这可能取决于make的版本):

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


@holms是bash语法。通过转义新行,可以将其作为单个bash命令进行处理。默认情况下,make新行将是新的bash命令。除了\ 在行尾有很多烦人的烦恼之外,这主要的警告是,每个命令都必须以;否则将在bash中隐含的终止。
flungo 2014年

1
正确的答案是@holms。“ \”和通配符都不是专门用于此目的的。使用通配符的原因是为了使代码清晰。这种方法不仅可读性差,而且更容易出现语法错误。
弥勒2015年

1
提供的链接@holms不工作了,使用gnu.org/software/make/manual/make.html#Conditionals代替
FERO

这是一个很好的答案,因为它可以与原始发问者的问题相匹配,并且将根据文件在构建目标时是否存在而不是在启动Makefile时是否存在而起作用,这是大多数人所期望的并且大部分时间都想要。在一些奇怪的情况下,来自@cnst的答案会更好。
迈克尔

30

或者像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
ThHat正是我想要做的。非常感谢。
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(未找到),则shell将不会执行echo命令,因此将与ifeq中的yes不匹配
Brad Dre

我认为您在解释说明中正确理解了这一点。
罗宾·许

@BradDre- echo -n yes将成功更改testyes不带NL 的字符串。然后,ifeq可以将其与硬编码进行比较yes。全部是因为ifeq要与字符串进行比较,而不是要从Shell命令获得成功状态。
杰西·奇斯霍尔姆 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前导破折号告诉make忽略任何错误。
杰西·奇斯霍尔姆

1
就我而言,忽略||。<错误操作>引起了问题。最后一个示例很好地解决了这个问题,如果文件不存在,则返回true。谢谢!
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/'

收到此错误后,我用谷歌搜索了如何检查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的答案使用\继续行和; 在shell中终止它们或像@kenorb中的将它们更改为一行一样是好的,它将解决此问题。

对于原始问题有一个更简单的答案(如@ alexey-polonsky指出的)。使用-f标志来rm,以便它不会触发错误

rm -f myApp

这更简单,更快,更可靠。请注意不要以斜杠和空变量结尾

rm -f / $(myAppPath) #从不这样做

您可能最终会删除系统。


1
对于删除OP具有的文件,这是一个很好的答案,但是我敢肯定,大多数发现此问题的人实际上并没有在寻找删除任何文件的方法。实际上,大多数答案甚至rm都根本没有提及,这证明了这一点。顺便说一句,我很确定rm -f /$(myAppPath)也不会造成任何损害,因为/它是目录,并且-r缺少。
cnst,

这不是一个简单的解决方案。就像@cnst所说的那样,原始张贴者可能不只是想做一个原因rm -f,例如他们可能想要rm一个变量。
阮丹(Dan Nguyen)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.