【问题标题】:Basic if else statement in MakefileMakefile 中的基本 if else 语句
【发布时间】:2020-02-24 10:01:14
【问题描述】:

我正在尝试在 Makefile 中执行一个简单的 if else 语句:

check:
  if [ -z "$(APP_NAME)" ]; then \
    echo "Empty" \
  else \
    echo "Not empty" \
  fi

当我执行make check 时出现以下错误:

if [ -z "" ]; then
/bin/bash: -c: line 1: syntax error: unexpected end of file
make: *** [check] Error 2

知道我做错了什么吗?

我知道我可以使用以下内容,但是在回声之后我有很多逻辑,所以我需要将它分散到多行:

check:
  [ -z "$(PATH)" ] && echo "Empty" || echo "Not empty"

【问题讨论】:

  • 在 echo 语句末尾添加分号

标签: bash if-statement makefile


【解决方案1】:

将您的 ma​​ke 目标更改为此(添加分号):

check:
    if [ -z "$(APP_NAME)" ]; then \
        echo "Empty"; \
    else \
        echo "Not empty"; \
    fi

要在没有换行符的情况下评估 shell 中的语句(换行符被反斜杠 \ 吃掉),您需要用分号正确结束它。您不能在 Makefile 中为条件 shell 脚本代码使用真正的换行符(参见 Make-specific background

[ -z "$(APP_NAME)" ], echo "Empty", echo "Not empty" 是所有需要评估的语句(类似于在终端中输入命令后按回车键)。

制作特定背景

ma​​ke 为一行中的每个命令生成一个新的 shell,因此您不能像使用真正的多行 shell 代码那样使用例如在脚本文件中。

把它带到一个极端,在 shell 脚本文件中可能会出现以下情况,因为 newline 充当命令评估(就像在终端中按 enter 是一个换行符,它评估输入命令):

if
[ 0 ]
then
echo "Foo"
fi

清单 1

如果你将它写在 Makefile 中,if 将在其自己的 shell 中进行评估(将 shell 状态更改为 if),之后技术上将评估条件 [ 0 ]再次在它自己的 shell 中,与之前的 if 没有任何联系。 但是,ma​​ke 甚至不会超过第一个 if,因为它希望退出代码继续执行下一条语句,而仅将 shell 的状态更改为 if 不会得到退出代码.

换句话说,如果 make-target 中的两个命令完全相互独立(没有任何条件),您可以完全通过普通换行符将它们分开,并让它们在各自的 shell 中执行.

因此,为了使 ma​​ke 正确评估多行条件 shell 脚本,您需要在一行中评估整个 shell 脚本代码(因此所有这些都在同一个 shell 中进行评估)。

因此,要在 Makefile 中评估 清单 1 中的代码,需要将其翻译为:

if \
[ 0 ]; \
then \
echo "Foo"; \
fi

最后一个命令 fi 不需要反斜杠,因为我们不需要再让生成的 shell 保持打开状态。

【讨论】:

    【解决方案2】:

    这是 shell 语法,而不是 makefile。您需要熟悉使用反斜杠在一行 shell 中输入长命令的规则。

    在您的示例中,删除反斜杠换行对后,它看起来像这样:

    if [ -z "$(APP_NAME)" ]; then echo "Empty" else echo "Not empty" fi
    

    也许您现在可以看到问题所在。 shell 将其解释为:

    if [ -z "$(APP_NAME)" ]; then
    

    后跟一个长命令:

    echo "Empty" else echo "Not empty" fi
    

    这将回显内容Empty else echo not empty fi,除了因为没有尾随fi shell 标记,而是语法错误。

    在 shell 语法中,您需要在每个单独的命令后添加分号,以便 shell 知道如何将其拆分:

    check:
            if [ -z "$(APP_NAME)" ]; then \
                echo "Empty"; \
            else \
                echo "Not empty"; \
            fi
    

    注意echo 命令后的分号告诉shell 命令参数到此结束。

    【讨论】:

      【解决方案3】:

      其他答案已经指出问题在于makefile设计和shell语法的结合。 Makefile 的设计使得编写复杂的食谱变得非常麻烦。通常,最好重新考虑该过程并重写部分 makefile 或将复杂性放在 shell 脚本中。

      这是你的食谱放在 shell 脚本中的例子:

      check:
        sh check.sh "$(APP_NAME)"
      

      和脚本:

      if [ -z "$1" ]; then
        echo "Empty"
      else
        echo "Not empty"
      fi
      

      优点:您拥有 shell 脚本的所有功能和灵活性,而没有任何 makefile 的尴尬。你只需要传递正确的参数。

      缺点:您的构建过程有额外的文件,并且您的 makefile 配方分布在多个文件中。

      如果条件是“简单的”,您可以使用 make 本身的条件构造。在你的情况下,我认为它只是简单到可以容忍,但如果再复杂一些,它将进入一个 shell 脚本。

      以下是如何使用 makefile 功能编写条件配方:

      check:
      ifdef APP_NAME
        echo "Empty"
      else
        echo "Not empty"
      endif
      

      再次添加注释

      check: # target name
      ifdef APP_NAME # makefile conditional syntax
        echo "Empty" # recipe if condition true
      else # makefile conditional syntax
        echo "Not empty" # recipe if condition false
      endif # makefile conditional syntax
      

      例如,如果定义了APP_NAME,则规则在执行过程中实际上如下所示:

      check:
        echo "Empty"
      

      这个特定的例子在语义上可能等同于您的 makefile。我不能肯定地说,因为我没有彻底测试。

      重要的是要知道在执行配方之前评估此条件。这意味着获得计算值的变量的值可能不同。

      优势:所有构建命令集中在一处。

      缺点:如果条件的行为不像你预期的那样,试图弄清楚 makefile 何时进行变量赋值和评估会让人头疼。

      阅读此处了解更多信息:

      另见

      【讨论】:

        最近更新 更多