Bash difference between |, &, || and &&

in #bash5 years ago (edited)

| : It is the pipe operator. It passes the stdout of first command to the next command.

$ echo a | echo b | echo c
c

`&`: It starts a asynchronous process.

||: It is like the boolean or operator. If the first half succeeds then don't executable the second half.

$ echo a || echo b
a

&&: It is like the boolean and operator. Executable both halves.

&& and || are straight forward. But | & executes the command as a separate script, only forwarding the stdout and stderr.

Examples:

  • echo a executes and prints a, with statuscode 0. Now since&& is and operator, echo b is also executed but its output is not displayed on terminal. Its output is followed as stdin to echo cwhich prints c.
$ echo a && echo b | echo c
a
c

  • asd is not a command and trying to execute it fails. Then echo b doesn't execute as && is and operator. Since, echo b doesn't execute there is nothing to pass through | pipe operator. Hence, echo c doesn't execute.
$ asd && echo b | echo c
zsh: command not found: asd

  • Executing asd fails and empty stdout of asd is passed via | operator resulting in execution of echo c.
$ asd |echo c
c
zsh: command not found: asd
  • asd fails resulting in echo b not being executed with overall statuscode 1. So, || being the or boolean operator executes second half echo c.
$ asd && echo b || echo c
zsh: command not found: asd
c

  • echo true executes. Passing true as stdin to asd command. But asd fails.
$ echo true | asd
zsh: command not found: asd

  • As echo a is async. A separate thread T is started for it and context moves to echo b, which prints b. Then T finishes and its stdout and stderr is displayed.
$ echo a & echo b

[1] 51810
b
a
[1]  + 51810 done       echo a 

  • d is created in root directory instead of the dir folder. This means cd cmd is executed in different environment or as a separate script and its stdout is piped resulting in touch d being executed.

This is similar to having a script a.sh with cd dir in it and running ./a.sh | touch d command.

$ ls
cmd/ 

$ cd cmd | touch d

$ ls
cmd/ d 

$ rm -rf d

$ echo cd cmd > a.sh && chmod +x a.sh

$ ./a.sh | touch d

$ ls
dir/ d

This behavior is similar to & , but & executes first cmd in separate thread.

Now, why I tried to figure out how these operator differs?

Reason is I was trying to execute many commands together as one-liner. But I needed to handle error as sometimes some of the commands failed . I tried using || , but if first command didn't fail then next command will be skipped. I tried |, but with cd dir | touch d, it generated d file in root directory instead of d directory. As with both | and &, the first command is executed in a different envionment or as a separate script, they aren't the best solution.

Conclusion: Instead of combining commands in one line , spread them in multiple lines. :)