7

One of legacy system got upgraded to bash4 and most of its scripts stopped working. I've narrowed it down to how a curly brackets are expanded within a <(cmdA ...|cmdB ... file{1,2}|cmdZ ...).

To illustrate the difference better:

BEFORE (bash 3.2.25):

[root@host1:~]$ bash -version|head -1
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
[root@host1:~]$ cat <(echo sort file{1,2})
sort file1
sort file2
[root@host1:~]$ join <(sed 's/\r//g;s/^[^:]*://' file{1,2}|LANG=C sort)
[root@host1:~]$

AFTER (bash 4.1.2):

[root@host2:~]$ bash --version|head -1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
[root@host2:~]$ cat <(echo sort file{1,2})
sort file1 file2
[root@host2:~]$ join <(sed 's/\r//g;s/^[^:]*://' file{1,2}|LANG=C sort)
join: missing operand after `/dev/fd/63'
Try `join --help' for more information.
[root@host2:~]$ 

Is it a "hard-coded" (and expected?) change made for bash4? Or is the behavior of this expansion controlled by some bash-level settings (like set -B/set +B) and can be switched back to old/legacy/bash3 mode? I'd rather change some shell-wide switch (instead of having to rewrite a pile of scripts).

If this (bash3) "feature" was cut off during a bugfix or improvement - I'm surprised, because old (bash3) syntax allowed to save a ton on typing...

Vlad
  • 1,157
  • 8
  • 15
  • 3
    Oh, wait. You expect two different *process substitutions* to be generated, not two arguments to `sed`? I don't know that that was **ever** in line with correct, documented behavior... which is to say, AFAIK, your code was depending on a bug. – Charles Duffy Mar 16 '17 at 22:26
  • @CharlesDuffy - that's how it always worked (for me and scripts) till bash4 upgrade - the `<(cmd {a,b})` did expand into two `<(cmd a) <(cmd b)` and was passed into join/etc. Those scripts are extensively using this feature, nesting it to multiple levels and scripts are tested (didn't want to touch it with my fat fingers and generate new errors). – Vlad Mar 16 '17 at 22:28
  • 3
    @Vlad That seems like a bug, which they fixed in Bash 4. You should expect the command inside `<(...)` to be parsed the same as any other command, so `<(cmd {a,b})` should be equivalent to `<(cmd a b)`. – Barmar Mar 16 '17 at 22:38
  • 1
    This kind of misunderstanding is one of the places where I appreciate Rich Hickey's policy in Clojure, *explicitly and loudly proclaiming* that while forwards compatibility for documented features is assured, under no circumstances is undocumented behavior to be relied on. – Charles Duffy Mar 16 '17 at 22:57

1 Answers1

5

The original behavior was undocumented (and contrary to the general rule that code enclosed in a process substitution, like a subshell or similar context, parses in the same manner in which it would have behaved outside of same).

As such, this was a bug, not a feature. This bug was fixed in bash-4.0-alpha. Quoting the CHANGES entry:

rr. Brace expansion now allows process substitutions to pass through unchanged.

No runtime flags to revert this change are available.

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    I was digging in the same direction in meantime, found another affected user (http://wiki.bash-hackers.org/syntax/expansion/brace), then found same entry in CHANGES and all the git commits which did lead to "commit bash-20080724 snapshot". For simplicity of my goal - I'll recompile the custom bash4 for my box while only reverting the priority-change, then will re-check the scripts. Sad it wasn't exposed to runtime flags to simplify my goal. Let me upvote all the answers and Thank You! – Vlad Mar 17 '17 at 00:40