3

One of the solutions to execute a bash script directly from a URL is:

bash <(curl -sS http://1.1.1.1/install) 

The problem is that when curl fails to retrieve the url, bash get's nothing as input and it ends normally (return code 0).

I would like the whole command to abort with the return code from curl.

UPDATE:

Two possible solutions:

curl -sS http://1.1.1.1/install | bash; exit ${PIPESTATUS[0]}

or:

bash <(curl -sS http://1.1.1.1/install || echo "exit $?")

The last one is kind of hackish (but shorter)

2
  • 2
    The problem is that bash will read directly from the temp pipefile, independently from what happen with the subshell. I sucessfully tried this workaround bash <(curl -sS http://1.1.1.1/install || echo "exit $?"). Is there a better solution? Commented Aug 20, 2015 at 12:58
  • 2
    From a security perspective, it's better to download the file and look at it before blindly passing it to bash for execution. Commented Aug 20, 2015 at 13:03

3 Answers 3

4

Try this to get curl's returncode:

curl -sS http://1.1.1.1/install | bash
echo ${PIPESTATUS[0]}
Sign up to request clarification or add additional context in comments.

6 Comments

Not equivalent if the fetched script needs to set variables in the current session but otherwise, yes. (Ignoring that this is a horrible "design" pattern that should never be used of course but that's not your fault.)
Using PIPESTATUS is a nice idea, but return code still 0. I was thinking something more like this: curl -sS http://1.1.1.1/install | bash; exit ${PIPESTATUS[0]}
Technically, if curl does succeed, you don't want to exit with its status; you want to exit with bash's status.
@mxlian: add curl's option --fail.
@chepner Bash will get nothing as input and will always exit with rc=0
|
2

Use a temporary file.

trap 'rm "$install"' EXIT
installer=$(mktemp)
curl ... > "$installer" || exit
# Ideally, verify the installer *before* running it
bash "$installer"

Here's why. If you simply pipe whatever curl returns to bash, you are essentially allowing unknown code to execute on your machine. Better to make sure that what you are executing isn't harmful first.

You might ask, how is this different from using a pre-packaged installer, or an RPM, or some other package system? With a package, you can verify via a checksum provided by the packager that the package you are about to install is the same one they are providing. You still have to trust the packager, but you don't have to worry about an attacker modifying the package en route (man-in-the-middle attack).

2 Comments

That's the point of <( ) syntax. To use a temporary pipefile
You can't look at the contents of process substitution before executing it, though.
1

You could save the output of the curl command in a variable and execute it only if the return status was zero. It might not be so elegant, but it's more compatible to other shells (doesn't rely on $PIPESTATUS, which isn't available on many shells):

install=`curl -sS http://1.1.1.1/install`
if [ $? -ne 0 ]; then
  echo "error"
else
  echo "$install" | bash
fi

2 Comments

If you're striving for portability, don't use [[ ... ]].
I had a feeling that comment would come in the moment I pressed the send button... :-) Changed it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.