如果第一个文件不存在,带有多个if/copy组合的post-build事件才会执行。

8

假设Delphi项目中的bin\目录包含文件Cert.pemKey.pem,下面的Delphi后构建事件仅在C:\Binaries\Cert.pem不存在时复制这两个文件:

if not exist $(OUTPUTDIR)Cert.pem (copy bin\Cert.pem $(OUTPUTDIR))
if not exist $(OUTPUTDIR)Key.pem (copy bin\Key.pem $(OUTPUTDIR))

只要 C:\Binaries\Cert.pem 存在,Key.pem 文件就不会被复制。

我该如何在 post-build 事件中解决这个问题?

编辑:与我的 2014 年的帖子 不同,这确实可以使用括号解决。请参见下面的答案。

2个回答

15

Delphi的后构建事件存在问题,因为它们不是批处理文件。

这意味着看起来像行的语句被Delphi IDE连接成一个大的用&符号分隔的语句。根据Command Redirection, Pipes - Windows CMD - SS64.com,这确保了按顺序执行命令:

commandA &  commandB      Run commandA and then run commandB
所以这是实际执行的语句:
if not exist $(OUTPUTDIR)Cert.pem (copy bin\Cert.pem $(OUTPUTDIR))&if not exist $(OUTPUTDIR)Key.pem (copy bin\Key.pem $(OUTPUTDIR))
问题在于第二个if现在被视为第一个if语句的“then”部分的延续:当$(OUTPUTDIR)Cert.pem存在时,第二个if永远不会执行。
有一个小众特性可以帮助解决这个问题,即将每个命令用括号括起来。通常情况下,这是为了允许一个命令跨越多行(特别是对于 iffor...do 循环),但它也适用于单行上。
将每个包含 if 语句的行都包含在括号中,确保它们成为独立的语句,不影响其他行,即使它们与 & 连接符连接。
在对话框中看起来像这样:
(if not exist $(OUTPUTDIR)Cert.pem (copy bin\Cert.pem $(OUTPUTDIR)))
(if not exist $(OUTPUTDIR)Key.pem (copy bin\Key.pem $(OUTPUTDIR)))

这样,IDE 就将其翻译成一个语句:

(if not exist $(OUTPUTDIR)Cert.pem (copy bin\Cert.pem $(OUTPUTDIR)))&(if not exist $(OUTPUTDIR)Key.pem (copy bin\Key.pem $(OUTPUTDIR)))

现在它按预期工作:

  • $(OUTPUTDIR)Cert.pem存在但$(OUTPUTDIR)Key.pem不存在时,只会复制$(OUTPUTDIR)Cert.pem
  • $(OUTPUTDIR)Cert.pem存在而$(OUTPUTDIR)Key.pem不存在时,只会复制$(OUTPUTDIR)Key.pem
  • 两者都不存在时,都会被复制
  • 两者都存在时,都不会被复制

我写2014年的文章 Delphi prebuild/prelink/postbuild events 时还不知道这个“技巧”,因此需要为其编写更新。

在搜索批处理文件括号时site:microsoft.com -site:social.technet.microsoft.com -site:answers.microsoft.com没有在官方文档中找到它,但是我并不惊讶,因为它变得歇斯底里了,而不是被设计出来。或者像老新东西属性h2g2一样:

就像宇宙一样,如果有人完全理解了Batch,则该语言将立即被更复杂和怪异的版本所取代。这显然已经发生过至少一次 ;)

我能找到的最好文档在Parenthesis/Brackets - Windows CMD - SS64.com上:

括号可用于跨多行拆分命令。 这可以使代码更具可读性。 变量将像单行命令一样为代码块评估。

 (command)

 (
  command
  command )

括号内会出错的事情 CMD shell 在评估括号时并不会使用任何智能。所以例如下面的命令将失败:

IF EXIST MyFile.txt (ECHO Some(more)Potatoes)
抱歉,我只能使用英语进行答复。

1
...并且要修复 IF EXIST MyFile.txt (ECHO Some(more)Potatoes),你只需要添加一个小的脱字符号 ... IF EXIST MyFile.txt (ECHO Some(more^)Potatoes)(好的,就是脱字符号...) - Magoo
1
能够解决我的问题,感谢详细的帖子! - HouseCat

5
使用多个构建事件而不是将两个命令放在同一个事件中。
执行构建事件的源代码行。 我可以很容易地简短说明:构建事件不是批处理文件。
发生的情况是,使用和符号(&)将构建事件中的所有行连接在一起,用于在一个命令行上执行多个命令。
这意味着所有花哨的控制结构(if语句、setlocal、for循环)在构建事件中都不可能实现。
参考:来自博客文章的粘贴内容:Delphi prebuild/prelink/postbuild events,作者为Jeroen W. Pluimers
让我想知道你为什么会问,因为看起来你在2014年已经写了答案。 :)

我本想自己回答这个问题,但中途接了一个电话。谢谢你来回答! - Jeroen Wiert Pluimers
1
终于有时间写下自己的答案了。这是可能的:使用括号。 - Jeroen Wiert Pluimers
1
@Jeroen,问题文本框下面有一个复选框“回答自己的问题”。现在我正在尝试改进该功能 - Victoria
2
@Victoria 好主意!当我提问时,我已经准备好了大部分答案,但被打断了,所以决定先发布帖子看看其他人是否有不同的解决方案,然后后来决定让答案更易读。请告诉我如何进一步改进它。 - Jeroen Wiert Pluimers
1
@Jeroen,啊哈,我个人会立即发布它们,因为这可能会导致困惑(就像在Brian的答案中链接到了你自己的博客文章一样 :) 你的回答写得很好!我会保持它的原样。已经点赞了。 - Victoria

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