URL: <https://savannah.gnu.org/bugs/?67750>
Summary: removal of special prefix chars under .ONESHELL
breaks multi-line strings
Group: make
Submitter: edquist
Submitted: Mon 01 Dec 2025 12:48:47 PM UTC
Severity: 3 - Normal
Priority: 5 - Normal
Item Group: Bug
Status: None
Privacy: Public
Assigned to: None
Open/Closed: Open
Discussion Lock: Unlocked
Component Version: 4.4.1
Operating System: POSIX-Based
Fixed Release: None
Triage Status: None
_______________________________________________________
Follow-up Comments:
-------------------------------------------------------
Date: Mon 01 Dec 2025 12:48:47 PM UTC By: Carl <edquist>
Hi there!
For POSIX-style shells, one use case for .ONESHELL is to allow passing
multi-line string arguments to a program, or similarly, to use a multi-line
here-doc as input for a program.
However, it appears that any combination of leading whitespace and special
prefix chars gets removed from multi-line string literals or here-docs.
(effectively, s/^[ \t@+-]*//gm)
This breaks correct behavior and is never what a user would want.
(A user adding .ONESHELL might lazily leave superfluous '@' prefix chars
for each command, but would never have put them for echo suppression in
the middle of a multi-line string or here-doc in the first place.)
This "special feature" is explained in the documentation for .ONESHELL
(5.3.1 Using One Shell). It says:
> As a special feature, if SHELL is determined to be a POSIX-style shell,
> the special prefix characters in "internal" recipe lines will be
> removed before the recipe is processed. This feature is intended to
> allow existing makefiles to add the .ONESHELL special target and still
> run properly without extensive modifications. Since the special prefix
> characters are not legal at the beginning of a line in a POSIX shell
> script this is not a loss in functionality.
Now first of all, special prefix characters (@, +, -) _are_ legal at the
beginning of lines -- you can have an executable in your PATH that starts
with one of these characters, for instance.
But more troubling, multi-line string literals or here-docs can include
program text for other interpreters (perl, python), where stripping these
characters (or leading whitespace) will break the program.
So there is definitely loss of functionality.
Version / platform info:
$ make --version
GNU Make 4.4.1
Built for x86_64-pc-linux-gnu
Here are makefile examples showing how removing leading whitespace in a
here-doc sent to python breaks the script, and likewise how removing @+-
characters in a multi-line string argument breaks a perl invocation.
(Makefile examples also attached.)
Python example:
$ cat oneshell.py.mk
SHELL = /bin/bash
.ONESHELL:
example:
python <<'EOT'
for x in "abc":
print(x)
EOT
Breaks because indentation matters:
$ make -f oneshell.py.mk
python <<'EOT'
for x in "abc":
print(x)
EOT
File "<stdin>", line 2
print(x)
^^^^^
IndentationError: expected an indented block after 'for' statement on line 1
make: *** [oneshell.py.mk:7: example] Error 1
Perl example:
$ cat oneshell.pl.mk
SHELL = /bin/bash
.ONESHELL:
example:
echo abc | perl -lwne '
/(a)?(b)?(c)?(d)?(e)?(f)?(g)?/;
print
@+ - @-, " unmatched capture buffers"
'
Breaks, removing the entire "@+ - @-" expression:
$ make -f oneshell.pl.mk
echo abc | perl -lwne '
/(a)?(b)?(c)?(d)?(e)?(f)?(g)?/;
print
, " unmatched capture buffers"
'
Useless use of a constant (" unmatched capture buffers") in void context at -e
line 3.
abc
Again, the problem is that make determines that bash is a POSIX-style shell,
and it assumes that means that all lines in its program text are for the
shell. But that is a bad assumption because the shell can contain text
for other interpreters or filters.
... My current work-around is to trick make into thinking that the SHELL
is not POSIX-style, by invoking bash indirectly through env:
SHELL = /usr/bin/env
.SHELLFLAGS = /bin/bash -c
Then you get the multi-line input verbatim, and the programs execute
properly.
Here is the correct output, using the 'env bash' workaround:
$ make -f oneshell.py.workaround.mk
python <<'EOT'
for x in "abc":
print(x)
EOT
a
b
c
$ make -f oneshell.pl.workaround.mk
echo abc | perl -lwne '
/(a)?(b)?(c)?(d)?(e)?(f)?(g)?/;
print
@+ - @-, " unmatched capture buffers"
'
4 unmatched capture buffers
...
I would suggest that this is both a documentation mistake (the special
feature _does_ result in loss of functionality of the modified recipes,
and the docs don't even mention that whitespace is also removed),
_and a bug_ (it breaks what should be valid multi-line POSIX-shell recipies).
It would have been better if this "special feature" of stripping
special prefix chars was opt-in (lazy programmers adding .ONESHELL
could have also made a .ONESHELL_REMOVE_LEADING_SPECIAL_PREFIX_CHARS,
or something, if they didn't want to manually remove the '@'
echo-suppresion prefix from internal recipe lines).
But supposing it's too late for that, and you don't want to break existing
lazily-authored makefiles in the wild, perhaps you could at least add an
opt-out target. Maybe something like .ONESHELL_VERBATIM ...
For now, the 'env bash' trick does work around the issue, but it feels
kind of silly :)
...
Thanks!
_______________________________________________________
File Attachments:
Example makefiles, with and without "env bash" workaround
Name: oneshell.pl.mk Size: 149B
<https://file.savannah.gnu.org/file/oneshell.pl.mk?file_id=57887>
Name: oneshell.pl.workaround.mk Size: 185B
<https://file.savannah.gnu.org/file/oneshell.pl.workaround.mk?file_id=57888>
Name: oneshell.py.mk Size: 94B
<https://file.savannah.gnu.org/file/oneshell.py.mk?file_id=57885>
Name: oneshell.py.workaround.mk Size: 130B
<https://file.savannah.gnu.org/file/oneshell.py.workaround.mk?file_id=57886>
AGPL NOTICE
These attachments are served by Savane. You can download the corresponding
source code of Savane at
https://savannah.gnu.org/source/savane-2753d22959ed8b20bdaba68a262c3d3b8776f354.tar.gz
_______________________________________________________
Reply to this item at:
<https://savannah.gnu.org/bugs/?67750>
_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/
signature.asc
Description: PGP signature
