Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minifying heredoc values in command substitutions produces invalid shell #923

Closed
andreykaipov opened this issue Oct 1, 2022 · 2 comments

Comments

@andreykaipov
Copy link
Contributor

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
Copy link
Contributor Author

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

@andreykaipov
Copy link
Contributor Author

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"

@mvdan mvdan closed this as completed in ca2088c Apr 15, 2023
diamondburned pushed a commit to diamondburned/mvdan-sh that referenced this issue May 9, 2023
Given the shell

  a=$(
    cat <<EOF
    hello
  EOF
  )

We would format it into

  a=$(cat \
  <<EOF)
    hello
  EOF

First, this makes bash unhappy, as it wants to find the closing EOF
before we close the command substitution. Teach rightParen to use a
newline if there are any pending heredocs, even if we're minifying.

Second, the escaped newline before the redirect is unnecessary.

Now, we format the shell into

  a=$(cat <<EOF
    hello
  EOF
  )

Thanks to Andrey Kaipov for the report and test cases.

Fixes mvdan#923.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant