【问题标题】:How to use shell builtin function from a Makefile?如何使用 Makefile 中的 shell 内置函数?
【发布时间】:2013-07-09 11:56:41
【问题描述】:

我只是偶然发现了这个问题。我试着写了一个很基础的Makefile目标:

core: myprogram
        ulimit -c 10000
        ./myprogram
        ulimit -c 0

这个想法是将核心大小限制设置为适当的值,使程序崩溃,然后将核心大小限制重置为零。当我调用此规则时,我收到以下错误消息:

$ make core
cc -Wall -Wextra -m32 -g  -o example example.c 
ulimit -c 100000
make: ulimit: Command not found
make: *** [core] Error 127

首先,我有点惊讶,但我认为问题在于ulimit 是一个内置的shell。而且,令人惊讶的是(至少对我而言),这些内置函数无法从 Makefile 调用。

此外,ulimit 既可以是一个内置函数(在我的 Debian 上就是这种情况)一个二进制程序(/usr/bin/ulimit)。

所以,我的问题很简单,如果可能的话,如何以优雅和便携的方式解决这个问题,并从 Makefile 内部调用内置函数?

【问题讨论】:

  • 不,ulimit 不是内置的 shell。 (考虑到它提供的功能,它怎么可能是一个?)
  • 那为什么我有一个/usr/bin/ulimit 可执行文件?
  • 这是两个不同的东西。就像test 或其他人一样。许多内置函数复制二进制程序。但是,这是首先使用的内置函数。

标签: shell makefile


【解决方案1】:

您收到此错误的原因是 make(尤其是 GNU make)尝试执行许多优化。其中之一是,如果命令 看起来 是一个不需要 shell 的简单命令,那么 make 将简单地通过 fork/exec 直接调用它而不运行 shell。如果该命令仅作为 shell 内置存在,那么这将不起作用。你的命令行ulimit -c 10000 是一个简单的命令,ulimit 没有被定义为只有 make 知道的 shell-builtin,所以 make 将尝试直接 fork/exec ulimit。因此,解决您当前问题的一种方法是简单地添加一个对 shell 特殊的字符(最明显的是;),这将暗示需要将该命令发送到 shell。

但是,这对你不起作用。

与上面 H2CO3 的评论完全相反:考虑到它提供的功能,它怎么可能是 [内置的 shell]? 你必须问自己的真正问题是相反的:如何考虑到它提供的功能,它可能不是一个吗? ulimit 的手册页明确指出:ulimit 实用程序应设置或报告对 shell 及其写入的文件施加的文件大小写入限制子进程,更进一步:由于ulimit影响当前的shell执行环境,它总是作为shell常规内置提供的。

您必须记住,UNIX 中的进程几乎不可能修改其父进程的任何方面。它只能修改自己或它调用的任何子进程。这包括环境变量、工作目录,还包括 ulimit 设置。

那么,很好,这如何适用于您的情况?您必须记住,make 配方中的每个逻辑行都是在 单独的 shell 中调用的。所以对于像这样的命令:

core: myprogram
        ulimit -c 10000 ;
        ./myprogram
        ulimit -c 0 ;

(添加分号以强制 shell)make 基本上调用的是这样的:

core: myprogram
        /bin/sh -c 'ulimit -c 10000 ;'
        /bin/sh -c './myprogram'
        /bin/sh -c 'ulimit -c 0 ;'

如您所见,每个 ulimit 都在其自己的 shell 中调用,因此它实际上是无用的。它将修改该外壳的核心文件大小限制,然后外壳退出并且更改丢失,然后在具有原始 ulimit 的新外壳中调用您的程序,然后调用第三个外壳并将内核的 ulimit 设置为 0 (也无用)。

您需要做的就是将所有这些命令放在一个逻辑行上,如下所示:

core: myprogram
        ulimit -c 10000; ./myprogram

(你不需要重新设置限制,因为shell无论如何都会退出)。

作为旁注,this 是 make 不太担心这些 shell 内置函数的原因。在您不需要需要使用一些特殊的shell字符(如分号)的上下文中,基本上不可能使用这样的内置函数。

【讨论】:

  • @perror:他是对的,你知道...我只是想到了一般的“内置与外部”问题,而不是具体的 ulimit 的含义。这应该是公认的答案。
  • 亲爱的 MadScientist,这真​​是一个绝妙的答案。而且,这也正是我所说的优雅。非常感谢!
  • 谢谢,你节省了我的时间。
【解决方案2】:

任何Makefile 的默认shell 是sh。如果您必须使用特定 shell 的内置函数,请在 Makefile 中指定该 shell:

SHELL:=bash

请注意,这是不好的 做法,因为这个想法是您的Makefile 应该在任何 机器上工作,而不仅仅是那些安装了bash 的机器。

如果您想支持任一变体(外部与内置),您可以检查bash 的可用性。 ulimit 通过which,并根据检查的结果设置一个包含要使用的命令的变量(ulimitbash -c "ulimit")。

编辑: MadScientist 关于ulimit 的“仅当前shell”方面是绝对正确的。出于文档目的,我将保持此答案不变,但它对 perror 的特定问题没有帮助。

【讨论】:

  • 我同意你的观点,这是一种不好的做法,因为它不可移植(我的一个用户正在使用带有sh 的 NetBSD,所以我不能按照你说的去做)。
  • @perror:您可以检查bash 的可用性。 ulimit 通过which,并根据结果设置一个包含要使用的命令的变量(ulimit vs. bash -c "ulimit")。
  • 是的,我会试试这个。但是,对我来说,这似乎有点像 brute-force 解决方案。真的没有希望做一些优雅的事情吗? :-)
  • @perror:这不是真正的蛮力。考虑:您处于sh 环境中。有两种方法可以得到你想要的,一种是通过可执行文件bash,一种是通过可执行文件ulimit。您必须检查哪一个可用......并且没有比通过which(专为此目的而构建的工具)“更优雅”的方式来做到这一点。
  • 好吧,brute-force 可能选得不好,但是这种情况枚举对我来说有点冒险。我们永远不知道有一天是否会出现一个新案件并打破一切。
【解决方案3】:

你可以说:

core: myprogram
        $(shell /bin/bash -c "ulimit -c 10000")
        ./myprogram
        $(shell /bin/bash -c "ulimit -c 0")

【讨论】:

  • 在命令行中真的需要$shell() 包装器吗?它不是用于变量赋值吗?
  • 再一次,这会强制使用/bin/bash,我需要一个既适用于具有二进制ulimit 和内置ulimit 的系统的解决方案......对不起。
  • 有比使用$(shell ...) 更简单(且更便携)的强制shell 方法,但无论如何这无济于事。看我的回答。
  • @MadScientist 我同意,建议的方法更像是一种 hack。
  • 这个坏了! Make 将整个 shell 命令块存储为单个递归扩展变量。在它决定必须运行该块时,它会扩展它。此时,both $(shell ...) 行已展开。现在 make 剩下一个字符串 ./myprogram,并将其传递给 shell(在 both ulimit 命令运行之后)
【解决方案4】:

在官方的 GNU make 文档(http://www.gnu.org/software/make/manual/make.html)第 5.3.2 章中,它说“用作 shell 的程序取自变量 SHELL。如果您的 makefile 中没有设置此变量,程序 /bin/ sh 用作外壳。” 您可以将 SHELL 设置为 /bin/bash。

【讨论】:

    【解决方案5】:

    简答:在命令末尾添加分号 (;)。这样,您将使用内置工具调用 FULL shell。

            ulimit -c 10000;
    

    而不是

            ulimit -c 10000
    

    【讨论】:

      猜你喜欢
      • 2014-01-28
      • 1970-01-01
      • 2012-09-28
      • 2013-10-13
      • 2016-04-20
      • 2012-04-18
      • 1970-01-01
      • 1970-01-01
      • 2012-11-26
      相关资源
      最近更新 更多