Rob Crowell
Rob Crowell

Reputation: 1485

Pipe a script into bash?

Is there a difference between:

$ bash ~/script.sh

and:

$ cat ~/script.sh | bash

They seem to behave slightly differently, and I can't quite figure out what is going on. My script.sh contains several lines of bash, but it seems to abort early when I pipe it into bash (but runs to completion when I run the script directly).

What's the difference in running the script in these two ways?

Furthermore, when I use <, the behavior is the same as the first example (runs to completion):

bash <( ~/script.sh )

The script is along the lines of

set -eux
ssh CLUSTER_0_SERVER_0 "do_something" || ssh CLUSTER_0_SERVER_1 "do_something"
ssh CLUSTER_1_SERVER_0 "do_something" || ssh CLUSTER_1_SERVER_1 "do_something"

Upvotes: 3

Views: 12671

Answers (1)

Mihai Maruseac
Mihai Maruseac

Reputation: 21435

Your ways of running the same script are all different in semantics.

See for example this simple script:

#!/bin/bash
echo $$
ssh 127.0.0.1
echo $$

Running it directly will execute all of the lines inside and both echo invocations will print the same PID: everything is a single process which runs the commands inside the script, one by one.

Running it via cat ./script.sh | bash creates two different processes in the beginning: one for doing the cat and one for bash. However, when the bash interpreter reads and executes the ssh command you get an error (if the ssh is not configured to not print it):

Pseudo-terminal will not be allocated because stdin is not a terminal

and then you get a login to the machine and it leaves immediately, killing the current process. Thus, the second value is a different PID. In your case, you have two ssh commands linked by a conditional operator. However, since after the first one the interpreter is killed there is no way the second command will be executed. That's why you'll get only the *_SERVER_0 ssh commands executed.

The third case works but only by mistake. The <( cmd ) construct means executing cmd and passing its output as a file argument to the caller. In your case, the script doesn't print anything so that's why you don't see any error. Running the example script you'll get errors like:

/dev/fd/63: line 1: 29355: command not found

The fourth case (suggested in the comments) bash < script is just the same as cat script | bash.

The proper way to run bash scripts is via bash script or ./script (and shebang). If you want to run the instructions in the same process you can also use . script (source) All others might work or not, depending on some lucky side effects (in general they should not work).

Upvotes: 5

Related Questions