简体   繁体   English

如何从Makefile中使用shell内置函数?

[英]How to use shell builtin function from a Makefile?

I just stumbled on this problem. 我只是偶然发现了这个问题。 I tried to write a very basic Makefile target: 我试着编写一个非常基本的Makefile目标:

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

The idea is to set up the core size limit to a proper value, make the program crash and then reset the core size limit back to zero. 我们的想法是将核心大小限制设置为适当的值,使程序崩溃,然后将核心大小限制重置为零。 When I am calling this rule, I get the following error message: 当我调用此规则时,我收到以下错误消息:

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

First, I have been a bit surprised, but I think that the problem is coming from the fact that ulimit is a shell builtin. 首先,我有点惊讶,但我认为问题来自于ulimit是内置shell的事实。 And, suprisingly (at least for me), these builtin functions cannot be called from a Makefile . 而且,令人惊讶的是(至少对我来说),这些内置函数无法从Makefile调用。

Moreover, ulimit can be both a builtin function only (it is the case on my Debian) or a binary program only ( /usr/bin/ulimit ). 而且, ulimit既可以是内置函数(在我的Debian上就是这种情况), 也可以只是二进制程序( /usr/bin/ulimit )。

So, my question is simple, how to workaround this problem, if possible in an elegant and portable manner , and call the builtin function from the inside of a Makefile ? 所以,我的问题很简单,如何以优雅和可移植的方式解决这个问题,并从Makefile内部调用内置函数?

The reason you're getting this error is that make (GNU make in particular) tries to perform a number of optimizations. 您收到此错误的原因是make(特别是GNU make)尝试执行许多优化。 One of those is that if the command appears to be a simple command that does not require the shell, then make will simply invoke it directly via fork/exec and not run a shell. 其中之一是,如果命令看起来是一个不需要shell的简单命令,那么make将直接通过fork / exec调用它而不运行shell。 If the command only exists as a shell built-in, then this will not work. 如果该命令仅作为内置shell存在,那么这将不起作用。 Your command line ulimit -c 10000 is a simple command and ulimit is not defined as only a shell-builtin that make knows about, so make will try to fork/exec ulimit directly. 你的命令行ulimit -c 10000是一个简单的命令,ulimit没有被定义为只有一个shell内置的make,所以make会尝试直接fork / exec ulimit So a way to get around your immediate issue is to simply add a character that's special to the shell (the most obvious one is ; ), which will hint to make that this command needs to be sent to the shell. 因此,解决您的直接问题的方法是简单地添加一个特殊于shell的字符(最明显的一个是; ),这将暗示要将此命令发送到shell。

However, this will not work for you. 但是,这对您不起作用。

Exactly contrary to H2CO3's comment above: How possibly could it be [a shell builtin], given the functionality it provides? 与H2CO3上面的评论完全相反: 考虑到它提供的功能,它怎么可能是[内置shell]? the real question you have to ask yourself is the opposite: how possibly could it NOT be one, given the functionality it provides? 你必须问自己的真正问题是相反的: 考虑到它提供的功能,它怎么可能不是一个? The man page for ulimit clearly states: The ulimit utility shall set or report the file-size writing limit imposed on files written by the shell and its child processes , and further: Since ulimit affects the current shell execution environment, it is always provided as a shell regular built-in. ulimit的手册页明确指出: ulimit实用程序应设置或报告对shell及其子进程写入的文件施加的文件大小写入限制 ,并且: 由于ulimit影响当前的shell执行环境,因此它始终以一个shell常规内置。

You have to remember that it's virtually impossible for a process in UNIX to modify ANY aspect of its parent process. 您必须记住,UNIX中的进程几乎不可能修改其父进程的任何方面。 It can only modify itself, or any child processes that it invokes. 它只能修改自身或它调用的任何子进程。 This includes the environment variables, working directory, and it also includes ulimit settings. 这包括环境变量,工作目录,还包括ulimit设置。

So, good, how does this apply to your situation? 那么,好,这对您的情况有何影响? You have to remember that each logical line in a make recipe is invokes in a separate shell . 您必须记住,make配方中的每个逻辑行都在一个单独的shell中调用。 So for a command like: 所以对于像这样的命令:

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

(adding the semicolons to force a shell) what make basically invokes is this: (添加分号以强制shell)基本上调用的是:

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

As you can see, each ulimit is invoked in its own shell, so it's effectively useless. 如您所见,每个ulimit都在自己的shell中调用,因此它实际上没用。 It will modify the core file size limit for that shell, then the shell exits and the change is lost, then your program is invoked in a new shell with the original ulimit, then a third shell is invoked and ulimit for cores is set to 0 (also useless). 它将修改该shell的核心文件大小限制,然后shell退出并且更改丢失,然后在具有原始ulimit的新shell中调用您的程序,然后调用第三个shell并将core的ulimit设置为0 (也没用)。

What you need to do is put all of these commands on a single logical line, like this: 您需要做的是将所有这些命令放在一个逻辑行上,如下所示:

core: myprogram
        ulimit -c 10000; ./myprogram

(you don't need to set the limit back, because the shell will exit anyway). (您不需要设置限制,因为shell无论如何都会退出)。

As a side note, this is why make doesn't worry too much about these shell builtins. 作为旁注, 就是为什么make不太担心这些shell内置函数。 A builtin like this is basically impossible to use to any effect in a context where you don't need to use some special shell character like a semicolon. 像这样的内建基本上不可能使用任何影响的情况下,你并不需要使用一些特殊的字符外壳像一个分号。

The default shell for any Makefile is sh . 任何Makefile的默认shell都是sh If you have to use builtins of a specific shell, specify that shell in your Makefile : 如果必须使用特定shell的内置函数,请在Makefile指定该shell:

SHELL:=bash

Note that this is bad practice, because the idea is that your Makefile should work on any machine, not only those with bash installed. 请注意,这是不好的做法,因为我们的想法是您的Makefile应该可以在任何机器上运行,而不仅仅是那些安装了bash机器。

If you want to support either variant (external vs. builtin), you can check for the availability of bash resp. 如果您想支持任何变体(外部与内置),您可以检查bash resp的可用性。 ulimit via which , and set a variable containing the command to use ( ulimit vs. bash -c "ulimit" ) depending on the outcome of that check. ulimit通过which ,并设置一个包含要使用的命令的变量( ulimitbash -c "ulimit" ),具体取决于该检查的结果。

Edit: MadScientist is absolutely right about the "current shell only" aspect of ulimit . 编辑: MadScientist对于ulimit的“当前shell”方面是完全正确的。 I'll keep this answer intact for documentation purposes, but it doesn't help with perror's specific problem. 为了文档的目的,我会完整地保留这个答案,但它对perror的具体问题没有帮助。

You can say: 你可以说:

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

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

Short answer : add a semicolon (;) on the end of the command. 简短回答 :在命令末尾添加分号(;)。 In this way you will invoke FULL shell with builtin tools. 通过这种方式,您将使用内置工具调用FULL shell。

        ulimit -c 10000;

instead of 代替

        ulimit -c 10000

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM