如何检查 Makefile 中是不是存在文件以便删除它?

Posted

技术标签:

【中文标题】如何检查 Makefile 中是不是存在文件以便删除它?【英文标题】:How do I check if file exists in Makefile so I can delete it?如何检查 Makefile 中是否存在文件以便删除它? 【发布时间】:2011-07-30 00:41:43 【问题描述】:

在我的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 是变量还是实际文件名? myApp 用于 myApplication 即构建过程的文件名。 如果您只是想避免在文件不存在时停止,rm -rf myApp 可能是一个替代方案。或者在命令前加上破折号 (-rm myApp) 以使 make 忽略来自 rm 的错误(但它会打印一条丑陋的消息)。 您的问题是 make 将规则中的每一行视为单独的命令,并将它们单独发送到 shell。这就像单独运行`if [ -a myApp ]'。如果您遇到此错误,您要么需要一个解决方案,将各行合并为一个(使用 ),要么最终使每行独立于另一行。现在有以下几个。 【参考方案1】:

缺少分号

if [ -a myApp ];
then
  rm myApp
fi

但是,我假设您在删除之前检查是否存在以防止出现错误消息。如果是这样,您可以使用“强制”删除的rm -f myApp,即如果文件不存在,则不会出错。

【讨论】:

这正是我想做的。非常感谢。 这在 Makefile 中不起作用,因为 if 仍然分布在多行中 - 您需要将其放在一行或使用 \es。即使您添加了 \ es,您仍然缺少一些分号。【参考方案2】:

它可能需要在行尾加一个反斜杠才能继续(尽管这可能取决于 make 的版本):

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

【讨论】:

似乎不是 Makefile 语法? sunsite.ualberta.ca/Documentation/Gnu/make-3.79/html_chapter/… @holms 它是一种 bash 语法。通过转义新行,它允许将其作为单个 bash 命令处理。默认情况下,make 中的一个新行将是一个新的 bash 命令。除了在行尾有很多 \ 的烦恼之外,主要的警告是每个命令都必须以 ; 终止,否则这将隐含在 bash 中。 正确答案是@holms。 '\' 和通配符都不完全用于此目的。使用通配符的原因是为了代码清晰。这种方法不仅可读性差,而且更容易出现语法错误。 @holms 提供的链接已失效,请改用gnu.org/software/make/manual/make.html#Conditionals 这是一个很好的答案,因为它与原始提问者所做的错误相匹配,并且它将根据文件在构建目标时是否存在而不是在 Makefile 时是否存在来工作开始,这是大多数人大部分时间所期望和想要的。在一些奇怪的情况下,@cnst 的答案会更好。【参考方案3】:

或者只是放在一行,就像make 喜欢的那样:

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

【讨论】:

在这种情况下这是一个很好的答案(并且应该得到支持)但如果操作更复杂,则效果不佳,在这种情况下只需切换到使用 \ [ -a myApp ] && rm myApp 错误/bin/sh: 1: [: -a: unexpected operator【参考方案4】:

看到这么多人为此使用 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

【讨论】:

试过了,但我一直收到Makefile:133: *** unterminated call to function `wildcard': missing `)'. Stop. 很好地使用了通配符,所以可以通过makefile本身来完成。整洁:) 有助于理解$(wildcard pattern) 的实际作用。 See link. 更简洁:FILE_EXISTS := $(or $(and $(wildcard $(PATH_TO_FILE)),1),0) 值得注意的是,如果您在 cygwin 下的 Windows 上运行,以这种方式使用通配符会在文件名上进行区分大小写的匹配,因此如果文件存在但大小写与路径不同'找不到。似乎没有任何方法可以在 makefile 中覆盖此行为。【参考方案5】:

问题是当您将命令拆分为多行时。因此,您可以在行尾使用\ 继续进行上述操作,也可以使用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

【讨论】:

我会投赞成票,但你没有警告-sexists and has a size greater than zero 的特例。所写的问题与大小无关,因此应该使用test -e 来检查文件是否存在,或者使用-d 来检查目录是否存在。空文件可能特别有用(因为需要更好的术语)指标/哨兵,这可能与 make 非常相关。 感谢您的建议。默认更改了-f,因为它更常用。 如何在 Windows 上获得test 为了让它工作,你需要在最后添加|| true,这样当文件不存在时命令返回true。 @AndrewMackenzie test -f myApp || CMD,注意||,所以如果-f 将失败 - 不存在 (||),然后运行命令。有意义吗?【参考方案6】:

一线解决方案:

   [ -f ./myfile ] && echo exists

带有错误操作的单行解决方案:

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

在我的make clean 指令中使用的示例:

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

make clean 工作时不会出现错误消息!

【讨论】:

只需执行@rm -f myfile。由于“-f”标志,无论文件是否存在,“rm”都会以0退出。 或者,-rm myfile 领先的破折号告诉 make 忽略任何错误。 就我而言,省略 || 导致问题。您的最后一个示例,如果文件不存在则返回 true,很好地解决了这个问题。谢谢! 对我来说,myfile 的路径必须带有撇号('):@[ -f 'myfile' ] && rm myfile【参考方案7】:
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 我认为您在解释声明中理解正确。 @BradDre -- echo -n yestest 的成功更改为字符串yes 没有NL。然后ifeq 可以将其与硬编码的yes 进行比较。这一切都是因为ifeq 想要一个字符串进行比较,而不是来自 shell 命令的成功状态。【参考方案8】:

第二个最佳答案提到ifeq,但是,它没有提到这个ifeq必须在makefile中与目标名称处于相同的缩进级别,例如,只有在没有文件的情况下才下载文件'当前不存在,可以使用以下代码:

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

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

【讨论】:

直到我在 if fi 之后添加反斜杠 `` 才起作用 这个答案会有点奇怪;文件检查发生在处理 makefile 时,但该操作将在稍后由 make 构建目标时发生。如果您同时删除该文件,则不会创建该文件。我已经进行了编辑以使其更清晰。 非常感谢!阅读手册并不清楚这一点。【参考方案9】:

像@mark-wilkins 的答案一样,使用 \ 来继续行和 ;在 shell 中终止它们或者像 @kenorb 中的那样将它们更改为一行是好的,并且会解决这个问题。

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

rm -f myApp

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

rm -f /$(myAppPath) #永远不要这样做

您最终可能会删除您的系统。

【讨论】:

这是删除 OP 拥有的文件的一个很好的答案,但我很确定大多数发现这个问题的人实际上并不是在寻找删除任何文件;事实上,大多数答案甚至根本没有提到rm,这实际上证明了这一点;顺便说一句,我很确定rm -f /$(myAppPath) 也不会造成任何损害,因为/ 是一个目录,而-r 不见了。 这不是一个更简单的解决方案。就像@cnst 说的那样,可能有一些原因导致原发者不想简单地做一个rm -f,例如他们可能想rm 一个变量。 我喜欢这个简单的答案。如果文件存在删除,如果不存在,不要抱怨!【参考方案10】:
test ! -f README.md || echo 'Support OpenSource!' >> README.md

“如果 README.md 不存在,什么都不做(并成功退出)。否则,将文本附加到末尾。”

如果您使用&& 而不是||,那么当文件不存在时会生成错误:

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

【讨论】:

【参考方案11】:

我在尝试:

[ -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中是否存在文件,这就是答案...

【讨论】:

【参考方案12】:
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

【讨论】:

【参考方案13】:

与问题略有不同,但如果您有一个包含要删除的文件列表的变量,您可以这样做

targets: filename1 filename2

clean_targets:
    @$(foreach file, $(targets), test -f $(file) && rm -v $(file) || echo No $(file);)

基本上,您循环遍历由目标变量定义的文件名,并使用“测试”检查目标是否存在。如果是,删除该文件,如果不是,报告它不存在。最后一次检查(报告它不存在)是必要的,否则在根本没有目标的情况下会引发错误

【讨论】:

【参考方案14】:

使用test命令检查文件是否存在,然后使用rm删除。\

file 命令的语法是 -

test -f FILENAME && echo exists || echo not exists

删除文件的语法是-

rm -rf FILENAME

所以现在我们需要一个命令来仅在文件存在时删除文件,所以我们将只使用 OR || 和 test 命令

test -f FILENAME || rm -rf FILENAME

use 可以通过在括号() 中使用和&& 来使用多个命令

test -f FILENAME || (rm -rf FILENAME && echo "file deleted as it exists")

【讨论】:

这在 makefile 中不起作用。不管第一个 test 结果如何,它总是会运行 rm -rf FILENAME

以上是关于如何检查 Makefile 中是不是存在文件以便删除它?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Makefile(vim)中的ifeq检查头文件是否位于main.c中

检查文件是不是存在于javascript中?

检查Java中是不是存在多个文件

如何检查用户是不是存在于数据库php中

如何检查 SQL 数据库中是不是存在函数?

如何检查子视图控制器是不是存在