32

Today I'm learning something about fifo with this article: Introduction to Named Pipes, which mentions cat <(ls -l).

I did some experiments by using sort < (ls -l), which pops out an error:

-bash: syntax error near unexpected token `('`

Then I found I misadded an extra space in the command.

But, why this extra command will lead to this failure? Why must the redirect symbol be close to the (?

  • It should be noted that *nix shells splits things up based on whitespace which creates the tokens that Alec mentioned. – chicks Sep 15 '15 at 23:39
45

Because that's not an <, it's a <() which is completely different. This is called process substitution, it is a feature of certain shells that allows you to use the output of one process as input for another.

The > and < operators redirect output to and input from files. The <() operator deals with commands (processes), not files. When you run

sort < (ls)

You are attempting to run the command ls in a subshell (that's what the parentheses mean), then to pass that subshell as an input file to sort. This, however, is not accepted syntax and you get the error you saw.

  • 3
    Your answer is good, but then sort is attempting to read the subshell as its input file → this is obviously wrong, as Bash will not even parse the syntax. Neither ls nor sort is actually run. – sleblanc Sep 11 '15 at 18:04
  • 1
    @sebleblanc fair point, rephrased the answer, thanks. – terdon Sep 11 '15 at 18:09
  • 1
    There's no sub-shell in this case. < (ls) is not a valid token here. – cuonglm Sep 12 '15 at 8:39
  • @cuonglm no, because bash treats it as a syntax error. My point is that (ls) would run ls in a subshell. – terdon Sep 12 '15 at 14:59
22

Because that's how it's meant to be.

<(...) in bash is the syntax for process substitution. It's copied from the same operator in ksh.

<, (, ), |, &, ; are special lexical tokens in bash that are used to form special operators in different combinations. <, <(, <<, <&... each have their role. < is for redirection. <file, < file would redirect input from a file. <'(file)' would redirect input from a file called (file), but <(file) is a different operator that is not a redirection operator.

< (file) would be < followed by (file). In that context, in bash, (file) is not valid. (...) can be valid as a single token in some contexts like:

(sub shell)
func () {
  ...
}
var=(foo bar)

But not in

sort < (cmd)

In the fish shell, it's different. In fish, (...) is for command substitution (the equivalent of $(...) in bash). And < is for input redirection like in Bourne-like shells.

So in fish:

sort <(echo file)

would be the same as:

sort < (echo file)

That is:

sort < file

But that's something completely different from bash's process substitution.

In the yash shell, another POSIX shell, <(...) is not for process substitution but for process redirection

In there,

sort <(ls -l)

Short for:

sort 0<(ls -l)

is a redirection operator. It's more or less equivalent to:

ls -l | sort

While in bash, the <(ls -l) is expanded to the path of a pipe, so it's more like:

ls -l | sort /dev/fd/0

In zsh, (...) is overloaded as a globbing operator ((*.txt|*.png) would expand to txt and png files) and as glob qualifier (*(/) for instance expands to directory files).

In zsh, in:

sort < (ls -l)

That (ls -l) would be treated as a glob qualifier. The l glob qualifier is to match on number of links and expects a number after l (as in ls -ld ./*(l2) would list the files with 2 links), so that's why you get a zsh: number expected error there.

sort < (w) would have given a zsh: no matches found: (w) error instead as (w) matches the files with empty name that are writeable.

sort < (w|cat) would have sorted the content of the w and/or cat files in the current directory...

  • why sort < $(ls -l) gives this error: bash: $(ls -l): ambiguous redirect – Edward Torvalds Sep 21 '15 at 16:08
  • @edwardtorvalds, because $(ls -l) expands to more than one word. Use quotes to prevent split+glob (sort < "$(echo file)"). Note that the behaviour or bash differs from that of POSIX sh in that bash does that split+glob there as well when non-interactive (not when called as sh though). – Stéphane Chazelas Sep 21 '15 at 16:13
  • by looking at ls -l | sort /dev/fd/0 I can say that output of ls -l is stored in /dev/fd/0 and sort command reads it to give the desired output. I am using tail -f --retry /dev/fd/0 to monitor that file but I am not getting any output. why? how can I read that file? – Edward Torvalds Sep 21 '15 at 16:34
  • In fish, you can use (foo | psub) to achieve input process substitution; there is no substitute (ha) for output process substitution yet. – Zanchey Sep 22 '15 at 23:47

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.