What's the difference between `curl | sh` and `sh -c "$(curl)"`?
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.)
I believe that they are practically identical. However, there are rare cases where they are different.
$(cmd)
gets substituted with the results of cmd
. Should the length of that result command exceeds the maximum argument length value returned by getconf ARG_MAX
, it will truncate the result, which may result in unpredictable results.
The pipe option does not have this limitation. Each line of output from the curl
command will be executed by bash
as it arrives from the pipe.
But ARG_MAX is usually in the 256,000 character range. For a docker install, I'd be confident using either method. :-)
In curl -sSL https://get.docker.com/ | sh
:
Both commands,
curl
andsh
, will start at the same time, in respective subshellsThe STDOUT from
curl
will be passed as the STDIN tosh
(this is what pipe,|
, does)
Whereas in sh -c "$(curl -sSL https://get.docker.com/)"
:
The command substitution,
$()
, will be executed first i.e.curl
will be run first in a subshellThe command substitution,
$()
, will be replaced by the STDOUT fromcurl
sh -c
(non-interactive, non-login shell) will execute the STDOUT fromcurl