17

As I understand it, pipe ( | ) takes the standard output of one process and passes it as standard input into another process.

But I want to know if pipe ( | ) is considered a command like ls, grep etc.

How many commands are in command line below?

ls /etc | grep nginx

I'm confused if I should count the pipe ( | )

14
  • 12
    Pipe is an operator, not a command.
    – Narzard
    Oct 4 at 14:14
  • 19
    If it was a command, you would be able to run it standalone: |. Try. Oct 4 at 14:25
  • 7
    How would the shell know where one command starts and another one begins?
    – gronostaj
    Oct 4 at 14:26
  • 5
    @doneal24 I understand that, but I'm trying to show OP how they could have tried to figure this out themselves. & being an ordinary command would make parsing tricky and somewhat arbitrary, which suggests it's maybe not the approach that designers would choose
    – gronostaj
    Oct 4 at 17:22
  • 1
    @clockw0rk if someone was asking if [ is a command, would you say it is not, because we can't find it in */bin/? ;)
    – PierU
    Oct 7 at 18:12

3 Answers 3

41

In Bash a pipe (| along with |&) is not a command, rather it is a control operator.

From this reference:

A pipeline is a sequence of one or more commands separated by one of the control operators ‘|’ or ‘|&’.

So in the context of your example, ls and grep would be the commands and you should not include the pipe.

11
  • 5
    ls is a command-line tool on par with grep. It is not a shell built-in. Both programs live in /bin or /usr/bin. There is considerable discussion on using type vs. which vs. command -v vs whence vs. a half-dozen other commands but some of these commands will tell you when a command is a shell built-in, when they are aliases, and when they are programs in /bin. The type command might be the best bet here. Compare type ls with type [[.
    – doneal24
    Oct 4 at 17:25
  • Ok, great to know. Thanks for clarifying that, I've removed that bit from my answer.
    – squillman
    Oct 4 at 17:27
  • 1
    You can get into difficulties when both test and echo are both shell built-in's on most shells and both exist in /usr/bin and they have different options/outputs depending on how you call them. Even worse, invoking /usr/bin/cd will change your directory for the duration of the command but the shell's cwd is not changed. The built-in cd will change the directory for the shell. Blame POSIX for requiring the existence of /usr/bin/cd. FYI, compgen -b will show all the shell built-ins on any POSIX-compliant shell.
    – doneal24
    Oct 4 at 17:35
  • 6
    @squillman A simple command is "a sequence of optional variable assignments and redirections, in any sequence, optionally followed by words and redirections, terminated by a control operator." Something LC_ALL=C grep '^[a-z]' < somefile, or indeed the ls /etc and grep nginx above are simple commands, but things like if, while constructs as well as { ...; } and ( ... ) blocks are compound commands instead.
    – ilkkachu
    Oct 4 at 17:45
  • 1
    @Landak GNU Bash manual -> 3.2.2 Pipelines: "If |& is used, command1’s standard error, in addition to its standard output, is connected to command2’s standard input through the pipe; it is shorthand for 2>&1 |. This implicit redirection of the standard error to the standard output is performed after any redirections specified by command1."
    – Andreas
    Oct 7 at 10:26
4

The proof is in actual testing. In bash:

|

returns:

-bash: syntax error near unexpected token `|'

while:

a|b

where a and b are unknown commands, returns:

-bash: b: command not found
-bash: a: command not found

with no reference to | because the pipe is perfectly fine when used between any two arbitrary commands, the problem is that these commands do not exist.

I suspect "b: command not found" comes first because in order the receiver (b) has to be running before the sender (a). If they were started (a) then (b), if (b) took longer to start up than (a) took to start sending, the data would have no place to go.

1
  • 1
    On my system (WSL Ubuntu) the command not found messages are outputted in random order, which probably means that both processes are launched at (almost) same time.
    – Jiří
    Oct 7 at 20:07
0

To add to the other answer: the pipe operator tells the shell that the two commands should be organized in a pipeline, as in the flow of text from one to the other. For that, the shell creates an anonymous pipe, i.e. a FIFO file-like channel, by calling the kernel. Then the shell runs the two commands, passing the input of the pipe to the first command as its stdout file descriptor, and the output of the pipe to the second command as its stdin. So text goes from stdout of the first command to stdin of the second one via the pipe.

If the pipe symbol denoted a separate command, that program would need to get hold of the processes of the other two commands somehow, then pass file descriptors for the anonymous pipe to them (assuming this is possible between processes that aren't parent and child), and those programs would need to swap their stdout and stdin with these descriptors, when they're already running—under the danger of potential loss of output in the meantime. This all is rather easier accomplished by the shell, which already knows the two processes, has special parent relationship with them, and can supply the file descriptors right at the start.

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.