3

I have a simple Bash script:

#!/usr/bin/env bash read X echo "X=$X" 

When I execute it with ./myscript.sh it works. But when I execute it with cat myscript.sh | bash it actually puts echo "X=$X" into $X.

So this script prints Hello World executed with cat myscript.sh | bash:

#!/usr/bin/env bash read X hello world echo "$X" 
  • What's the benefit of executing a script with cat myscript.sh | bash? Why doesn't do it the same things as if I execute it with ./myscript.sh?
  • How can I avoid Bash to execute line by line but execute all lines after the STDIN reached the end?
13
  • 3
    Why not use bash ./myscript.sh Commented Nov 20, 2014 at 17:52
  • 2
    or just ./myscript.sh Commented Nov 20, 2014 at 17:53
  • 1
    Because it's actually more complicated. I want to write a simple installer to download a shell script with curl and then pipe it to bash to avoid a creation of a temp file, like Composer does it with PHP (getcomposer.org/download). But if this way doesn't work I also need to do it with PHP or with a temp file. Commented Nov 20, 2014 at 17:59
  • @TheFox Then look at Ethan's answer, exactly to the phrases "Where would you like read to read data from in this setup? You can manually do that (if you can define such a place)." If there is a human looking at the terminal, you can have read stdin redirected to /dev/tty, as it was just NOW answered by Charles... Commented Nov 20, 2014 at 18:05
  • 2
    @TheFox Knowing why someone is asking a question helps determine if they are asking the right question. What you want to do and what you should do are sometimes two different things. Commented Nov 20, 2014 at 19:02

2 Answers 2

8

Instead of just running

read X 

...instead replace it with...

read X </dev/tty || { X="some default because we can't read from the TTY here" } 

...if you want to read from the console. Of course, this only works if you have a /dev/tty, but if you wanted to do something robust, you wouldn't be piping from curl into a shell. :)


Another alternative, of course, is to pass in your value of X on the command line.

curl https://some.place/with-untrusted-code-only-idiots-will-run-without-reading \ | bash -s "value of X here" 

...and refer to "$1" in your script when you want X.

(By the way, I sure hope you're at least using SSL for this, rather than advising people to run code they download over plain HTTP with no out-of-band validation step. Lots of people do it, sure, but that's making sites they download from -- like rvm.io -- big targets. Big, easy-to-man-in-the-middle-or-DNS-hijack targets).

Sign up to request clarification or add additional context in comments.

Comments

1

When you cat a script to bash the code to execute is coming from standard input.

Where does read read from? That's right also standard input. This is why you can cat input to programs that take standard input (like sed, awk, etc.).

So you are not running "a script" per-se when you do this. You are running a series of input lines.

Where would you like read to read data from in this setup?

You can manually do that (if you can define such a place). Alternatively you can stop running your script like this.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.