16

In Bash version 4.2.47(1)-release when I try to catenate formatted text that comes from a HERE-dcoument like so:

cat <(fmt --width=10 <<FOOBAR
(I want the surrounding parentheses to be part of the HERE-document)
(Even the preceding unbalanced parenthesis should be part of it.
FOOBAR
) # I want this paranthesis to end the process substitution.

I get the following error:

bash: bad substitution: no closing `)' in <(fmt --width=10 <<FOOBAR
(I want the surrounding parentheses to be part of the HERE-document)
(Even the preceding unbalanced parenthesis should be part of it.
FOOBAR
)

Also I do not want to quote the HERE-document, i.e. write <'FOOBAR', because I still want to have variables being substituted within it.

4
  • Do you really need the cat call? Why not leave it at calling fmt?
    – iruvar
    Jun 17 '14 at 0:52
  • 2
    I must admit it is a contrived example. My actual needs are more complex than that.
    – Tim Friske
    Jun 17 '14 at 0:55
  • 1
    It is interesting that when you replace (Even with "(Even" it works. It is same for \(Even. Looks like a parsing bug. Bash is still in a context were it is looking for braces while also in the context of reading the here doc and both contexts contradict each other. Jun 17 '14 at 8:40
  • 1
    This is fixed in bash 4.3, incidentally.
    – chepner
    Sep 9 '15 at 15:30
7

The process substitution is roughly equivalent to this.

Example - mechanics of process substitution

Step #1 - make a fifo, output to it

$ mkfifo /var/tmp/fifo1
$ fmt --width=10 <<<"$(seq 10)" > /var/tmp/fifo1 &
[1] 5492

Step #2 - read the fifo

$ cat /var/tmp/fifo1
1 2 3 4
5 6 7 8
9 10
[1]+  Done                    fmt --width=10 <<< "$(seq 10)" > /var/tmp/fifo1

The use of parens within the HEREDOC also seems OK:

Example - just using a FIFO

Step #1 - output to FIFO

$ fmt --width=10 <<FOO > /var/tmp/fifo1 &
(one)
(two
FOO
[1] 10628

Step #2 - read contents of FIFO

$ cat /var/tmp/fifo1
(one)
(two

The trouble, I believe you're running into is that the process substitution, <(...), doesn't seem to care for the nesting of parens within it.

Example - process sub + HEREDOC don't work

$ cat <(fmt --width=10 <<FOO
(one)
(two
FOO
)
bash: bad substitution: no closing `)' in <(fmt --width=10 <<FOO
(one)
(two
FOO
)
$

Escaping the parens seems to appease it, a little:

Example - escaping parens

$ cat <(fmt --width=10 <<FOO                 
\(one\)
\(two
FOO
)
\(one\)
\(two

But doesn't really give you what you want. Making the parens balanced also seems to appease it:

Example - balancing parens

$ cat <(fmt --width=10 <<FOO
(one)
(two)
FOO
)
(one)
(two)

Whenever I have complex strings, such as this to contend with in Bash, I almost always will construct them first, storing them in a variable, and then use them via the variable, rather than try and craft some tricky one liner that ends up being fragile.

Example - use a variable

$ var=$(fmt --width=10 <<FOO
(one)
(two
FOO
)

Then to print it:

$ echo "$var"
(one)
(two

References

5

This is an old question, and as you realize that this is a contrived example (and thus that the correct solution is to use cat | or actually, no cat at all in this case), I'll just post my answer for the general case. I would solve it by putting it in a function and using that instead.

fmt-func() {
    fmt --width=10 <<FOOBAR
(I want the surrounding parentheses to be part of the HERE-document)
(Even the preceding unbalanced parenthesis should be part of it.
FOOBAR
}

and then use that

cat <(fmt-func)
1
  • This is a great solution to supplying scripts as files to interpreters (awk, grep, node, python, etc.) so that they can accept stdin for processing. It is also great for passing configs to commands like aws with the --cli-input-json flag. Aug 6 '20 at 10:19
3

This is just a workaround. Pipe fmt to cat instead of using process substitution

fmt --width=10 <<FOOBAR | cat 
(I want the surrounding parentheses to be part of the HERE-document)
(Even the preceding unbalanced parenthesis should be part of it.
FOOBAR
2
  • 1
    I tried your "workaround" and it would work for me. Thanks. But still I want to grasp why my combination of a HERE-document nested inside a process substitution does not work. Have you got an answer?
    – Tim Friske
    Jun 17 '14 at 1:01
  • @TimFriske, I'm going to have to defer that one to one of the bash wizards on this site. My knowledge of bash parser internals is limited to say the least
    – iruvar
    Jun 17 '14 at 1:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.