bmatcuk
bmatcuk

Reputation: 465

bash: ambiguous redirect when using curly-bracket globbing in process substitution

The following contrived example works:

$ while read -r line; do echo $line; done < <(ls -al *.js)
...

The following, however, gives an error:

$ while read -r line; do echo $line; done < <(ls -al {package.json,package-lock.json})
-bash: 0: ambiguous redirect

despite the fact that what is inside the process substitution works fine:

$ ls -al {package.json,package-lock.json}
-rw-r--r--  1 me  staff   764K Apr 19 10:23 package-lock.json
-rw-r--r--  1 me  staff    10K Apr 19 10:23 package.json

Why is that?

Upvotes: 3

Views: 248

Answers (1)

Gordon Davisson
Gordon Davisson

Reputation: 125788

This looks like a bug (or at least oddity) in bash v3, but it's fixed in v4. What's happening is that the brace expansion ({package.json,package-lock.json}) is being applied to the entire process substitution, not just an argument within it. Here's a simplified example I'll use to demo what's going on:

$ cat < <(echo {a,b})
-bash: 0: ambiguous redirect

Let's try some variants of this:

$ cat <(echo {a,b})    # This works, sort of... each arg winds up on a separate line
a
b
$ echo <(echo {a,b})    # echo shows *two* different process substitutions are being done
/dev/fd/63 /dev/fd/62
$ grep -H . <(echo {a,b})    # This prints where it gets each line, showing it gets one letter/line from each substitution
/dev/fd/63:a
/dev/fd/62:b

...so what it looks like is that <(echo {a,b}) is getting brace-expanded to <(echo a) <(echo b) instead of <(echo a b). This works with cat and grep because they accept multiple input files, but the < redirect can only take one input file (so you get an "ambiguous redirect" error).

This seems to be fixed in bash v4 (I've tested in v4.2.10 and 4.4.19):

$ echo $BASH_VERSION 
4.4.19(1)-release
$ cat < <(echo {a,b})
a b
$ cat <(echo {a,b})
a b
$ echo <(echo {a,b})
/dev/fd/63

Upvotes: 4

Related Questions