mvdan / sh

A shell parser, formatter, and interpreter with bash support; includes shfmt
https://pkg.go.dev/mvdan.cc/sh/v3
BSD 3-Clause "New" or "Revised" License
7.21k stars 339 forks source link

Minifying heredoc values in command substitutions produces invalid shell #923

Closed andreykaipov closed 1 year ago

andreykaipov commented 2 years ago

For example, running the following file through shfmt -mn:

#!/bin/sh
a=$(
        cat <<EOF
    hello
EOF
)
echo "$a"

Produces the following shell:

#!/bin/sh
a=$(cat \
<<EOF)
    hello
EOF
echo "$a"

Which can't be ran:

sh: line 4: warning: here-document at line 4 delimited by end-of-file (wanted `EOF')
sh: line 3: warning: here-document at line 3 delimited by end-of-file (wanted `EOF')
sh: line 4: hello: command not found
sh: line 5: EOF: command not found

For some context, I'm using this kind of pattern to assign literal strings to a variable. I can use something like read -r -d '' a <<'EOF' in bash, but afaik assigning cat <<'EOF' is the way to do something like that with posix sh.

andreykaipov commented 2 years ago

So this behavior is actually not just with command substitutions, but also exists for heredocs in subshells and in process substitutions too.

andreykaipov commented 2 years ago

Digging through the code I've at least figured a workaround:

#!/bin/sh
a=$(
        cat <<EOF
    hello
EOF
        :
)
echo "$a"

Adding an extra statement after the ending heredoc word allows the formatter to correctly flush the heredoc, I'm guessing because of the new line. Now running shfmt -mn produces:

#!/bin/sh
a=$(cat \
<<EOF
        hello
EOF
:)
echo "$a"