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.1k stars 336 forks source link

syntax: support {varname} redirects #719

Open jmcantrell opened 3 years ago

jmcantrell commented 3 years ago

The shfmt tool altering the following valid code, making it invalid:

coproc foo {
    echo test
}

exec {foo[1]}>&-  # converts to: exec {foo[1]} >&-
mvdan commented 3 years ago

I'm not sure I follow what the exec line is doing. What's the syntax there, and where is it documented?

jmcantrell commented 3 years ago

It's closing the file descriptor stored at index 1 of the foo array. The previous coproc command puts file descriptors into the array.

The syntax for both closing a file descriptor and providing the file descriptor using a variable are described in the second paragraph here: https://www.gnu.org/software/bash/manual/bash.html#Redirections

And since there can't be any space between the file descriptor and the redirection, it produces the error.

mvdan commented 2 years ago

Ah I see, thank you. I was not aware you could close file descriptors stored inside a variable or array. So the parser indeed thinks that you wrote exec {foo[1]} >&-. This will require a bit of an internal refactor to add support for the syntax.

For my own convenience, here's the relevant bit of the docs:

Each redirection that may be preceded by a file descriptor number may instead be preceded by a word of the form {varname}. In this case, for each redirection operator except >&- and <&-, the shell will allocate a file descriptor greater than 10 and assign it to {varname}. If >&- or <&- is preceded by {varname}, the value of varname defines the file descriptor to close. If {varname} is supplied, the redirection persists beyond the scope of the command, allowing the shell programmer to manage the file descriptor’s lifetime manually.

jippi commented 6 months ago

I just got hit by this one as well 😄 Is there a way to disable this behavior for a file or a line? currently it blocks any usage of shfmt due to breaking flock usage like this

exec {lock_fd}>"$file"
# (...)
exec {lock_fd}>&-

being turned into

exec {lock_fd}>"$file"
# (...)
exec {lock_fd} >&-  # notice the space here

Perhaps some other workaround?