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?
TheFox
  • 502
  • 10
  • 26
  • 3
    Why not use `bash ./myscript.sh` – anubhava Nov 20 '14 at 17:52
  • 2
    or just `./myscript.sh` – Karoly Horvath Nov 20 '14 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 (https://getcomposer.org/download/). But if this way doesn't work I also need to do it with PHP or with a temp file. – TheFox Nov 20 '14 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... – gboffi Nov 20 '14 at 18:05
  • http://stackoverflow.com/questions/5735666/execute-bash-script-from-url – vp_arth Nov 20 '14 at 18:11
  • @vp_arth, ...the answers to which aren't quite on-point for this, as they propose not using stdin at all. – Charles Duffy Nov 20 '14 at 18:14
  • Hm, what used here? `curl -s http://server/path/script.sh | bash /dev/stdin arg1 arg2` I think, `/dev/stdin` is `stdin`, or not? – vp_arth Nov 20 '14 at 18:20
  • @KarolyHorvath You actually don't need do know *why* I'm doing this. I want to do it this way. I wouldn't have asked if I would do it with `bash ./myscript.sh` or `./myscript.sh`. – TheFox Nov 20 '14 at 18:25
  • 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. – chepner Nov 20 '14 at 19:02
  • @vp_arth, ahh; just so. I kept seeing `<()` used, which would be a `/dev/fd/##` link on modern Linux. – Charles Duffy Nov 20 '14 at 19:21
  • @chepner But @karoly-horvath's second answer wasn't constructive. And even his first answer was not really helpful because I **already** wrote that I tried to execute the script with `./myscript.sh`. So what's the benefit for @karoly-horvath's first answer? Nothing. Why should I ask a question for this problem if I see that it's also working after executing it with `./myscript.sh` and **even if I already wrote it into my question** that I tried it this way? – TheFox Nov 20 '14 at 19:57
  • Sometimes it seems that the people on SO doesn't read my questions very carefully. Or answering only to get the fu...ng points and not because of the actual problem. Asking why someone is doing it this way actually doesn't contribute to solve the question. And as you can see no one of the first persons who answered actually solved my problem. And in the end it makes no difference if you pipe it from `curl` or from `cat`. What's the *right* question for this question in your opinion? – TheFox Nov 20 '14 at 19:58
  • My question was *Why does it work with A but not with B and what's the difference between A and B?* and you people came and said *Try it with A*. This isn't constructive. I tried A but A isn't the problem. B is the problem. – TheFox Nov 20 '14 at 20:14

2 Answers2

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).

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
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.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148