Docker 的一种简单安装方法(例如)是这样的:
curl -sSL https://get.docker.com/ | sh
不过,我也看到过一些类似这样的内容(使用 Docker 示例):
sh -c "$(curl -sSL https://get.docker.com/)"
它们在功能上似乎相同,但是有理由使用其中一种而不是另一种吗?或者这只是一种偏好/审美?
(值得注意的是,运行来自未知来源的脚本时要非常小心。)
答案1
存在实际差异。
curl -sSL https://get.docker.com/ | sh
启动curl
,sh
同时将 的输出curl
与 的输入连接起来sh
。curl
将按照sh
运行脚本的速度(大致)执行下载。服务器可以检测时序中的不规则情况当简单地将资源下载到文件或缓冲区中或在浏览器中查看资源时,注入不可见的恶意代码。
在 中sh -c "$(curl -sSL https://get.docker.com/)"
,严格在运行curl
之前运行。在启动sh
之前,资源的全部内容将被下载并传递到您的 shell 。sh
您的 shell 仅sh
在curl
退出时启动,并将资源文本传递给它。服务器无法检测到sh
呼叫;它仅在连接结束后启动。这类似于首先将脚本下载到文件中。
(这可能与 docker 情况无关,但通常可能是一个问题,并突出了两个命令之间的实际差异。)
答案2
我相信它们实际上是相同的。然而,在极少数情况下它们会有所不同。
$(cmd)
被替换为 的结果cmd
。如果该结果命令的长度超过 返回的最大参数长度值getconf ARG_MAX
,它将截断结果,这可能会导致不可预测的结果。
管道选项没有此限制。命令的每一行输出将在从管道到达时curl
执行。bash
但 ARG_MAX 通常在 256,000 个字符范围内。对于 docker 安装,我有信心使用任何一种方法。 :-)
答案3
在curl -sSL https://get.docker.com/ | sh
:
两个命令
curl
和sh
都将在各自的子 shell 中同时启动来自的 STDOUT
curl
将作为 STDIN 传递给sh
(这就是管道、|
、 的作用)
而在sh -c "$(curl -sSL https://get.docker.com/)"
:
命令替换
$()
, 将首先执行,即curl
首先在子 shell 中运行命令替换 ,
$()
将被 STDOUT 替换curl
sh -c
(非交互式、非登录 shell)将执行 STDOUTcurl
答案4
两者之间的一个区别(取自网络上的其他答案)是,如果您不立即下载整个脚本,它可能会在未知点切断脚本的一半,并将命令的含义更改为被执行。所以看来先下载整个文件然后再评估会更好。