在makefile中测试目录是否存在

98

在他的回答中,@Grundlefleck解释了如何检查目录是否存在。我尝试将其用于makefile中,如下所示:

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then
        echo "Dir exists"
    fi

执行 make foo.bak(假设存在 foo.bar 文件)会出现以下错误:

echo "foo"
foo
if [ -d "~/Dropbox" ]; then
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [foo.bak] Error 2

我的解决方法是创建一个独立的Bash脚本,在该脚本中实现测试,并从makefile中调用该脚本。不过,这个方法听起来非常繁琐。有没有更好的方式可以在makefile中检查目录是否存在?


1
你可以尝试这样做:https://dev59.com/tnVD5IYBdhLWcg3wE3Ro#99188 - Ricardo Cristian Ramirez
7个回答

114

make命令,如果是shell命令,则必须在一行上,或者使用反斜杠进行多行扩展。所以,这种方法将起作用:

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then echo "Dir exists"; fi

或者

foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then \
        echo "Dir exists"; \
    fi

1
看起来问题是所有内容都需要在一行上,这是真的吗? - Patrick Collins
@PatrickCollins 单个命令可以在不同的行上。例如,echo "foo"后跟mv bar bah可以是不同行上的命令。然而,shell会将if表达式一直看作单个命令,直到fi,因此它必须在一行上,或者有“转义”(\)换行符。正如您在答案中所看到的,echo "foo"可以在if表达式之前作为单独的命令放在另一行上。 - lurker
1
@PatrickCollins 简而言之,是的。每行都在自己的 shell 中运行,因此您不能将测试放在一行上,下面的块将在不同的 shell 中。可以使用行继续字符“\”跨多行格式化,这实际上将其全部放在一行上。 - Edwin Buck
; 是必须的,这样也可以: 如果 [ -d ${BOOTSTRAPDIR} ]; then
echo "找到 ${BOOTSTRAPDIR}";
else
echo "未找到 ${BOOTSTRAPDIR}";
fi
- tom
右边的:上简单的依赖关系写成~/Dropbox/.会起作用吗?makefile首先是一个文件测试器。 - undefined
@Sandburg我的回答已经有10年了。我认为你是正确的。我确信有多种好的方法来回答原始问题。 - undefined

66

这种方法可以最小化回响:

.PHONY: all
all:
ifneq ($(wildcard ~/Dropbox/.*),)
        @echo "Found ~/Dropbox."
else
        @echo "Did not find ~/Dropbox."
endif

11
$(wildcard ~/Dropbox/.) 就足够了。在这种情况下,通配符不是必需的。 - Daniel Alder
对我来说,直到我删除了*通配符,它才真正起作用,原因不明。 - Kzqai
在我的情况下:ifneq "$(wildcard path/$(var)/*)" "" @echo "目录已存在" else @echo "做些什么" endif - K8sCat

53

在没有目录的情况下采取行动

如果您只需要知道目录不存在并希望通过创建目录来采取行动,例如,您可以使用普通的 Makefile 目标:

directory = ~/Dropbox

all: | $(directory)
    @echo "Continuation regardless of existence of ~/Dropbox"

$(directory):
    @echo "Folder $(directory) does not exist"
    mkdir -p $@

.PHONY: all

注:

  • | 表示 make 不关心时间戳(它是一个 仅作为顺序前提条件)。
  • 你可以写 false 退出,或者以其他方式解决问题,而不是写 mkdir -p $@

如果你还需要在目录存在时运行一系列特定的指令,则无法使用上述方法。换句话说,它等同于:

if [ ! -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does not exist"
fi

这里没有else语句。

根据目录的存在进行操作

如果你想要相反的if语句也是可能的:

directory = $(wildcard ~/Dropbox)

all: | $(directory)
    @echo "Continuation regardless of existence of ~/Dropbox"

$(directory):
    @echo "Folder $(directory) exists"

.PHONY: all $(directory)

这相当于:

if [ -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does exist"
fi

再次强调,没有else语句。

对目录的存在和不存在进行操作

这变得有点麻烦,但最终为两种情况提供了很好的目标:

directory = ~/Dropbox
dir_target = $(directory)-$(wildcard $(directory))
dir_present = $(directory)-$(directory)
dir_absent = $(directory)-

all: | $(dir_target)
    @echo "Continuation regardless of existence of ~/Dropbox"

$(dir_present):
    @echo "Folder $(directory) exists"

$(dir_absent):
    @echo "Folder $(directory) does not exist"

.PHONY: all

这相当于:
if [ -d "~/Dropbox" ]; then
    echo "The ~/Dropbox folder does exist"
else
    echo "The ~/Dropbox folder does not exist"
fi

自然情况下,通配符扩展可能比if-else语句慢。不过,第三种情况可能相当罕见,只是为了完整性而添加的。

感谢您提供精彩的代码!不过,如果我需要处理多个$(目录),该怎么办呢? - Seung-Woo Lee
@Seung-WooLee 那将会是更进一步的操作。 :-) 在这种情况下,我会主张使用带有.ONESHELL特殊目标的shell本身来提高可读性。 - Anne van Rossum
测试是否可以在目录本身上进行(就像您的示例中那样),还是应该测试~/Dropbox/.以获得更好的结果? - undefined

18

试一下这个:

.PHONY: all
something:
    echo "hi"
all:
    test -d "Documents" && something

只有当Documents存在时,才会执行something下的命令。

为了解决评论中提到的问题,您可以这样定义一个变量:

PATH_TEST = ~/SomeDirectory

test -d $(PATH_TEST) && something

16

我曾遇到这样一个问题,需要根据目录是否存在来定义一个变量,但是在Makefile的最高层级,之前提到的方法并不能解决。我在这里找到了一个好的解决方案,可以像这样使用:

MY_DIRNAME=../External
ifneq "$(wildcard $(MY_DIRNAME) )" ""
  # if directory MY_DIRNAME exists:
  INCLUDES += -I../External
else
  # if it doesn't:
  INCLUDES += -I$(HOME)/Code/External
endif

这将根据存储在MY_DIRNAME中的目录是否存在来修改变量INCLUDES

(动机:在我的情况下,此变量将在稍后由第一个包含的另一个Makefile中使用:

include $(SFRAME_DIR)/Makefile.common

我希望以简单的方式在两个不同的环境中使用相同的Makefile


5

有一个非常不同的答案,可以让您在一个shell中按照您设想的方式使用if语句:

.ONESHELL:
foo.bak: foo.bar
    echo "foo"
    if [ -d "~/Dropbox" ]; then
        echo "Dir exists"
    fi

请注意,唯一的区别是 ONESHELL 特殊目标

1
我使用以下代码来检测文件或目录是否存在并进行相应操作:
$(if $(filter expected,$(wildcard *)), the expected file exists)

根据您的请求:

.PHONY: ~/Dropbox

~/Dropbox:
    echo "Dir exists"

foo.bak: foo.bar | $(if $(filter ~/Dropbox,$(wildcard ~/*)), the expected file exists)

这可以进一步简化:

foo.bak: foo.bar | $(wildcard ~/Dropbox)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接