Shell – What’s the difference between `curl | sh` and `sh -c “$(curl)”`

command-substitutioncurldockerpipeshell

One easy install method for Docker (for example) is this:

curl -sSL https://get.docker.com/ | sh

However, I have also seen some that look like this (using the Docker example):

sh -c "$(curl -sSL https://get.docker.com/)"

They appear to be functionally the same, but is there a reason to use one over the other? Or is it just a preference/aesthetic thing?

(Of note, be very careful when running script from unknown origins.)

Best Answer

There is a practical difference.

curl -sSL https://get.docker.com/ | sh starts curl and sh at the same time, connecting the output of curl with the input of sh. curl will carry out with the download (roughly) as fast as sh can run the script. The server can detect the irregularities in the timing and inject malicious code not visible when simply downloading the resource into a file or buffer or when viewing it in a browser.

In sh -c "$(curl -sSL https://get.docker.com/)", curl is run strictly before the sh is run. The whole contents of the resource are downloaded and passed to your shell before the sh is started. Your shell only starts sh when curl has exited, and passes the text of the resource to it. The server cannot detect the sh call; it is only started after the connection ends. It is similar to downloading the script into a file first.

(This may not relevant in the docker case, but it may be a problem in general and highlights a practical difference between the two commands.)