Shell status codes in make

2020-06-08 10:12发布

问题:

I use a Makefile (with GNU make running under Linux) to automate my grunt work when refactoring a Python script. The script creates an output file, and I want to make sure that the output file remains unchanged in face of my refactorings.

However, I found no way to get the status code of a command to affect a subsequent shell if command.

The following rule illustrates the problem:

check-cond-codes:
    diff report2008_4.csv report2008_4.csv-save-for-regression-testing; echo no differences: =$$!=
    diff -q poalim report2008_4.csv; echo differences: =$$!=

The first 'diff' compares two equal files, and the second one compares two different files. The output is:

diff report2008_4.csv report2008_4.csv-save-for-regression-testing; echo no differences: =$!=
no differences: ==
diff -q poalim report2008_4.csv; echo differences: =$!=
Files poalim and report2008_4.csv differ
differences: ==

So obviously '$$!' is the wrong variable to capture the status code of 'diff'. Even using SHELL := /bin/bash at beginning of the Makefile did not solve the problem.

A variable returning the value, which I need, would (if it exists at all) be used in an 'if' command in the real rule.

The alternative of creating a small ad-hoc shell script in lieu of writing all commands inline in the Makefile is undesirable, but I'll use it as a last resort.

Related:

  • How to make a failing shell command interrupt make

回答1:

I think you're looking for the $? shell variable, which gives the exit code of the previous command. For example:

$ diff foo.txt foo.txt
$ echo $?
0

To use this in your makefile, you would have to escape the $, as in $$?:

all:
    diff foo.txt foo.txt ; if [ $$? -eq 0 ] ; then echo "no differences" ; fi

Do note that each command in your rule body in make is run in a separate subshell. For example, the following will not work:

all:
    diff foo.txt foo.txt
    if [ $$? -eq 0 ] ; then echo "no differences" ; fi

Because the diff and the if commands are executed in different shell processes. If you want to use the output status from the command, you must do so in the context of the same shell, as in my previous example.



回答2:

Use '$$?' instead of '$$!' (thanks to 4th answer of Exit Shell Script Based on Process Exit Code)



回答3:

Don't forget that each of your commands is being run in separate subshells.

That's why you quite often see something like:

my_target:
    do something \
    do something else \
    do last thing.

And when debugging, don't forget the every helpful -n option which will print the commands but not execute them and the -p option which will show you the complete make environment including where the various bits and pieces have been set.

HTH

cheers,



回答4:

If you are passing the result code to an if, you could simply do:

all:
    if diff foo.txt foo.txt ; then echo "no differences" ; fi


回答5:

The bash variable is $?, but why do you want to print out the status code anyway?



回答6:

Try `\$?' I think the $$ is being interpreted by the makefile