From the docs:
.ONESHELL
If .ONESHELL is mentioned as a target, then when a target is
built all lines of the recipe will be given to a single invocation
of the shell rather than each line being invoked separately (*note
Recipe Execution: Execution.).
So, a makefile, like:
.ONESHELL :
all ::
echo 'foo
bar'
Running, I get:
$ make
echo 'foo
/bin/sh: 1: Syntax error: Unterminated quoted string
makefile:4: recipe for target 'all' failed
make: [all] Error 2 (ignored)
Trying, with almost the same makefile, but adding a - prefix to the recipe, to ignore errors, as documented:
To ignore errors in a recipe line, write a '-' at the beginning of
the line's text (after the initial tab). The '-' is discarded before
the line is passed to the shell for execution.
The makefile, like:
.ONESHELL :
all ::
-echo 'foo
bar'
Running, I get:
$ make
echo 'foo
/bin/sh: 1: Syntax error: Unterminated quoted string
makefile:4: recipe for target 'all' failed
make: [all] Error 2 (ignored)
bar'
/bin/sh: 1: Syntax error: Unterminated quoted string
makefile:4: recipe for target 'all' failed
make: [all] Error 2 (ignored)
Why?
GNU Make 3.81 does not support .ONESHELL, but 3.82 does.
$ /usr/gnu/bin/make --version
GNU Make 3.82
$ /usr/bin/gmake --version
GNU Make 3.81
$ cat gnu.mk
.ONESHELL:
all:
echo 'foo' "$$$$"
echo 'bar' "$$$$"
$ /usr/bin/gmake -f gnu.mk
echo 'foo' "$$"
foo 3100
echo 'bar' "$$"
bar 3101
$ /usr/gnu/bin/make -f gnu.mk
echo 'foo' "$$"
echo 'bar' "$$"
foo 3103
bar 3103
$
I deduce that you're using GNU Make 3.81 from 2006 (or possibly an earlier version, but 3.80 is from 2002); 3.82 is from 2010. The current version is 4.1 from 2014. The online documentation applies to the current version. While lots of the material also applies to older versions, not all of it does.
I also observe that the original makefile with the single single quote on the first line and another on the second seems to cause trouble even with GNU Make 3.82. However, when the lines are each OK, it does seem to work. That's a bit puzzling. And, now I've also installed GNU Make 4.1 (I'd already got it downloaded, but hadn't built it), it too has problems with the bust.mk file below.
$ cat bust.mk
.ONESHELL:
all:
echo 'foo
bar' "$$$$"
$ /usr/gnu/bin/make -f bust.mk
echo 'foo
/bin/sh: -c: line 0: unexpected EOF while looking for matching `''
/bin/sh: -c: line 1: syntax error: unexpected end of file
make: *** [all] Error 2
$
I'm not sure I understand how GNU Make gets confused by that. It might be worth filing a bug for it. OTOH, I'm not sure I'm going to get worried about it, either. But you'll need to take this odd behaviour into account in your future testing.
Related
I want to compute the time it takes to finish my command in makefile.
Here is what I tried, but it doesn't work:
FILE = some_file.6.txt
.ONESHELL:
another_file.txt: ${FILE}
#START=$$(date +%s.%N)
#END=$$(date +%s.%N)
#echo $$(($$END-$$START))
Here is the error I get:
$ make
/bin/sh: 3: arithmetic expression: expecting EOF: "1569658240.437512688-1569658240.436685866"
makefile:5: recipe for target 'another_file.txt' failed
make: *** [another_file.txt] Error 2
I've tried all combination of adding/removing both ( and $.
Please help, thanks.
This is not a makefile problem. It's a shell problem. This is trivially seen by running the command at a shell prompt rather than a makefile, and you'll get the same error:
$ /bin/sh -c 'echo $((1569658240.437512688-1569658240.436685866))'
/bin/sh: 1: arithmetic expression: expecting EOF: "1569658240.437512688-1569658240.436685866"
It's also an error in bash, so setting SHELL := /bin/bash won't help:
$ /bin/bash -c 'echo $((1569658240.437512688-1569658240.436685866))'
/bin/bash: 1569658240.437512688-1569658240.436685866: syntax error: invalid arithmetic operator (error token is ".437512688-1569658240.436685866")
If you check the documentation for your shell you'll see that arithmetic expressions only work on integer values, not floating point values as you're attempting above.
To perform more advanced math including on floating point values, you should investigate the bc program:
.ONESHELL:
another_file.txt: ${FILE}
#START=$$(date +%s.%N)
#END=$$(date +%s.%N)
#echo $$END-$$START | bc
I wanted to determine the version of the Intel Fortran compiler in my makefile, so I added some script using GNU shell function as below for testing,
VERIFORT := $(shell ifort --version)
#VERIFORT := $(shell ifort --version | grep ^ifort) # error occurred too
.PHONY: test
test:
echo $(VERIFORT)
If you copy those code lines shown above, make sure there is a tab before the echo command.
which gives me some errors
/bin/sh: -c: line 0: syntax error near unexpected token `('
When I ran the command ifort --version or ifort --version | grep ^ifort in a terminal, it gave proper result and no error occurred.
My system: 64-bit CentOS 7
Appreciate any correction suggestions.
[EDIT]
Add more output details:
With the grep version of VERIFORT, the make command produced the following result,
echo ifort (IFORT) 18.0.2 20180210
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `echo ifort (IFORT) 18.0.2 20180210'
make: *** [test] Error 1
[SOLVED]
It turns out to be an echo-usage problem as mentioned by #MadScientist
I think you need to quote the value of the VERIFORT variable when you print it, so that the shell doesn't interpret special characters.
Quoting the VERIFORT variable produced the following result (the grep version)
echo 'ifort (IFORT) 18.0.2 20180210'
ifort (IFORT) 18.0.2 20180210
and no error occurred.
I also tested it by using echo in a terminal
echo ifort (IFORT) 18.0.2 20180210
Which generated the same error
bash: syntax error near unexpected token `('
It seems you didn't show the complete output of the make command. I think before this error message, make printed an echo line (unless the makefile you showed us isn't actually what you invoked, and your actual makefile adds a # before the echo... in which case you should remove it while you debug). If you'd shown us what that output was it would be more clear what the problem is. Also you didn't show what the output of the ifort --version command is when you run it from the command line, but I think it probably contains parentheses.
I think you need to quote the value of the VERIFORT variable when you print it, so that the shell doesn't interpret any special characters:
test:
echo '$(VERIFORT)'
I try to run 'diff' in Makefile, using anonymous pipes. Different results
are observed comparing launching 'diff' from bash shell and from Makefile. Any clarification ? Thanks.
$ diff <(echo cat) <(echo dog)
1c1
< cat
---
> dog
$ make
diff <(echo cat) <(echo dog)
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `diff <(echo cat) <(echo dog)'
make: *** [test] Error 2
My Makefile is
test:
diff <(echo cat) <(echo dog)
As indicated by the error messages, your shell is sh not bash; so you cannot use Bash syntax features.
A common workaround is to set SHELL=/bin/bash (or your local equivalent) or, of course, refactor your code into POSIX-compliant shell script.
On many websites, "$" is written at the beginning when introducing the Linux command.
But of course, this will result in a "$: command not found" error.
To avoid this it is necessary to delete or replace "$" every time, but it is troublesome.
So, if the beginning of the input command is "$", I think that it would be good if I could ignore "$", is it possible?
If you really need this, you can create a file in a directory that is in your $PATH. The file will be named $ and will contain
#!/bin/bash
exec "$#"
Make it executable, then you can do
$ echo foo bar
foo bar
$ $ echo foo bar
foo bar
$ $ $ echo foo bar
foo bar
$ $ $ $ echo foo bar
foo bar
Note that this does not affect variable expansion in any way. It only interprets a standalone $ as the first word in the command line as a valid command.
I just noticed a problem with this: It works for calling commands, but not for shell-specific constructs:
$ foo=bar
$ echo $foo
bar
$ $ foo=qux
/home/jackman/bin/$: line 2: exec: foo=qux: not found
and
$ { echo hello; }
hello
$ $ { echo hello; }
bash: syntax error near unexpected token `}'
In summary, everyone else is right: use your mouse better.
Yes it is possible for you to ignore the command prompt, when copying commands from web sites. Use the shift and arrow keys to ignore the prompt. This will also help you to ignore the use of the # sign, which is used to indicate commands, which need administrative privileges.
I am writing a bash script and need to redirect the stdout and stderr output of a command i run to a single file, prefixing each line with stderr or stdout, accordingly.
is there a simple way to do this?
annotate-output, from Debian's devscripts, does this.
The example in its man page:
$ annotate-output make
21:41:21 I: Started make
21:41:21 O: gcc -Wall program.c
21:43:18 E: program.c: Couldn't compile, and took me ages to find out
21:43:19 E: collect2: ld returned 1 exit status
21:43:19 E: make: *** [all] Error 1
21:43:19 I: Finished with exitcode 2
Try this:
(myCommand | sed s/^/stdout:/ >> myLogfile) 2>&1 | sed s/^/stderr:/ >> myLogFile
The first pipe inserts a stdout: prefix to the standard output of myCommand and appends it to myLogFile.
The parenthesis are used to make a single command of all of that. They tell that further redirections apply to what is inside parenthesis and not to sed only.
Then standard error is redirected to standard output with 2>&1 (remember that original standard output has already been redirected to a myLogFile). Second pipe inserts a stderr: prefix to it and appends it to myLogFile.