`curl |有什么区别?sh`和`sh -c“ $(curl)”`?


23

例如,一种简单的Docker安装方法是:

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

但是,我也看到了一些类似于Docker的示例:

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

它们在功能上似乎相同,但是是否有理由在一个之上使用一个?还是仅仅是偏爱/审美?

(值得注意的是,运行来自未知来源的脚本时要格外小心。)

Answers:


39

有实际的区别。

curl -sSL https://get.docker.com/ | sh启动curlsh同时将的输出curl与的输入连接shcurl将以大约sh可以运行脚本的速度(大致)执行下载。当将资源简单地下载到文件或缓冲区中或在浏览器中查看时,服务器可以检测到时间的不规律性并注入不可见的恶意代码。

在中sh -c "$(curl -sSL https://get.docker.com/)"curl严格在运行之前sh运行。在sh启动之前,将下载资源的全部内容并将其传递给您的Shell 。您的外壳程序仅shcurl退出时启动,并将资源文本传递给它。服务器无法检测到sh呼叫;它仅在连接结束后才启动。这类似于先将脚本下载到文件中。

(这在docker情况下可能不相关,但通常可能是一个问题,突出了这两个命令之间的实际区别。)


2
谢谢,这似乎是两者之间最重要的区别。
萨克(Sarke)

您能否引用一个资源来支持此声明?我会对了解服务器如何检测“ sh”调用非常感兴趣。
阿尔弗雷德·阿姆斯特朗

1
@AlfredArmstrong Uhm,我在该vulnerable to server-side detection短语上添加了一个链接。它导致了一篇博客文章,其中详细解释了他们如何实现它。TL; DR:在脚本中入睡,并观察服务器上的接收延迟。
乔纳斯·谢弗

1
@JonasWielicki谢谢-链接不是很清楚-不是您的错,我认为是SE的CSS。人们非常狡猾,不是吗?:)
Alfred Armstrong

1
所有这些使我想知道是否有人曾经尝试在现实中做过这种花招而没有立即被抓住。但这并不是很重要,因为即使没有这些技巧,您也可能只是让脚本执行了恶意操作,而没有完全阅读该脚本的人将很容易受到攻击。
ilkkachu

11

我相信它们实际上是相同的。但是,在少数情况下它们会有所不同。

$(cmd)替换为的结果cmd。如果该result命令的长度超过所返回的最大参数长度值getconf ARG_MAX,它将截断结果,这可能导致不可预测的结果。

管道选项没有此限制。curl命令的每一行输出都将从bash管道到达时执行。

但是ARG_MAX通常在256,000个字符范围内。对于docker安装,我有信心使用这两种方法。:-)


有趣,所以有区别。谢谢
Sarke

1
如有疑问,请使用管道方法。我没有在答案中指定首选项,但是我更喜欢使用管道方法进行此类使用,因为您永远不知道有多少数据通过管道。
格雷格·塔尔萨

2
“它将截断结果”-Shell应该为此发出错误消息,而不是静默截断。测试时,我什至从下面的shell中得到一个错误ARG_MAX,当getconf ARG_MAXprint 时,bash将我系统上的单个参数限制为131072字节2097152。但是无论是错误还是截断,它都不起作用。
hvd

但是Bourne shell的旧实现具有较低的限制。在4.2BSD中,限制为10240个字符,而在较早的系统上,该限制甚至更低。.当然是30年前,所以今天您不太可能遇到如此低的限制。如果我没记错的话,其中一些早期的shell只是默默地截断了。
AndyB

一个参数的128 kB限制是Linux的事情,与Bash无关。
ilkkachu

8

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)将从以下位置执行STDOUT: curl


1
那么,有什么真正的区别吗?
萨克(Sarke),

@Sarke是的,理论上就像我提到的那样,但几乎没有引起注意。(如果不使用命令替换,将产生可见的效果。)
heemayl

1
@Sarke,如果未完全下载脚本,则不一定会通过管道注意到它。通过管道传递到的进程可以忽略该信号。
Janus Troelsen

@JanusTroelsen您未完全下载是什么意思?为什么会发生这种情况,但服务器错误除外,在这种情况下将没有任何内容传递给sh。
hasufell

还是连接中断...传输失败的方法有很多
Janus Troelsen

0

两者之间的区别(取自网络上的其他答案)是,如果您不立即下载整个脚本,则它可能会在未知位置切断脚本的一半,并将命令的含义更改为被执行。因此,似乎首先下载整个文件,然后评估它会更好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.