Search code examples
bashshellrhelcurly-bracesexpansion

Why bash4 expands curly braces differently?


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...


Solution

  • 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.