Makefile while loop try/catch 相当于先用 conda 安装 python 依赖然后用 pip

Makefile while loop try/catch equivalent to install python dependencies first with conda then with pip

我需要 运行 一个 while 循环来安装 Python 依赖项。在 Python 世界中,最近有 2 种安装依赖项的方法已经建立:

"pseudocode"应该是:

在 Python 世界中,依赖项在 requirements.txt 文件中指定,通常是精确版本 (==) 每行一个依赖项,模式为 <MY_DEPENDENCY>==<MY_VERSION>

等效的 bash 所需命令是:while read requirement; do conda install --yes $requirement || pip install $requirement; done < requirements.txt,但是由于我不完全了解的原因,这在 GNU make/Makefile 世界中不起作用得到.

我尝试了几种不同的 while 循环 - 都没有成功。基本上,一旦 conda 命令失败,我就无法继续进行 pip 尝试。我不确定为什么会发生这种情况(因为它在 "normal bash" 中有效)并且我找不到管理某种低级 try/catch 模式的方法(对于那些熟悉高级编程语言的人)。

这是我最后一次尝试,但没有成功,因为它在 conda 失败时停止:

foo-target:
    # equivalent to bash: conda install --yes $requirement || pip install $requirement;
    while read requirement; do \
        conda install --yes $requirement ; \
        [ $$? != 0 ] || pip install $requirement; \
    done < requirements.txt

我如何确保首先使用 conda 尝试在 requirements.txt 中安装每个要求,当 conda 失败时,然后使用 pip?

为什么我的代码不起作用?我看到有人指出 shbash 之间的差异,但我无法隔离问题。

编辑:

我最终在 Makefile 中使用 bash 命令解决了问题,但是 我发现这个解决方案并不理想,因为我还需要维护单行 bash 脚本中的另一段代码(见下文),有没有办法将所有内容保存在 Makefile 中,完全避免 bash?

Makefile目标:

foo-target:
    bash install-python-dependencies.sh

bash一行脚本:

#!/usr/bin/env bash
while read requirement; do conda install --yes $requirement || pip install $requirement; done < requirements.txt

我可以直接从命令行 运行 脚本 (bash),我也可以 运行 它来自 Makefile,但是 我想摆脱 bash 脚本并始终执行 make foo-target 而不使用 bash(即使在 Makefile 内部也避免 bash ).

如上所示,您的 makefile 将按预期工作,除了您必须转义 shell 变量中的 $,例如 $$requirement.

我无法通过简化示例来模拟行为来重现您的问题:

foo-target:
        for i in 1 2 3; do \
            echo conda; \
            test $$i -ne 2; \
            [ $$? -eq 0 ] || echo pip; \
        done

给出预期的输出:

$ make
conda
conda
pip
conda

您是否已将 .POSIX: 目标添加到您的 makefile 中,但此处未显示?如果我这样做,那么我就会得到你声称看到的行为:

conda
make: *** [Makefile:2: foo-target] Error 1

原因在 the manual for .POSIX 中描述:

In particular, if this target is mentioned then recipes will be invoked as if the shell had been passed the '-e' flag: the first failing command in a recipe will cause the recipe to fail immediately.

如果您想保持 .POSIX 模式但又不想出现此错误,最简单的方法是使用您在第一个示例中展示的方法;我不知道你为什么停止使用它:

foo-target:
        while read requirement; do \
            conda install --yes $$requirement || pip install $$requirement; \
        done < requirements.txt