Qual é a diferença entre `curl | sh` e `sh -c“ $ (ondulação) ”`?

23

Um método de instalação fácil para o Docker (por exemplo) é o seguinte:

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

No entanto, também vi alguns que se parecem com isso (usando o exemplo do Docker):

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

Eles parecem ser funcionalmente iguais, mas existe um motivo para usar um sobre o outro? Ou é apenas uma coisa de preferência / estética?

(Observe, tenha muito cuidado ao executar scripts de origens desconhecidas.)

Sarke
fonte

Respostas:

39

Há uma diferença prática.

curl -sSL https://get.docker.com/ | shinicia curle sh, ao mesmo tempo, conecta a saída de curlcom a entrada de sh. curlrealizará o download (aproximadamente) o mais rápido que shpuder executar o script. O servidor pode detectar irregularidades no tempo e injetar código malicioso não visível ao simplesmente fazer o download do recurso em um arquivo ou buffer ou ao visualizá-lo em um navegador.

Em sh -c "$(curl -sSL https://get.docker.com/)", curlé executado estritamente antes da shexecução. Todo o conteúdo do recurso é baixado e passado para o seu shell antes do shinício. Seu shell só inicia shquando curlsai e passa o texto do recurso para ele. O servidor não pode detectar a shchamada; só é iniciado depois que a conexão termina. É semelhante ao download do script em um arquivo primeiro.

(Isso pode não ser relevante no caso da janela de encaixe, mas pode ser um problema em geral e destaca uma diferença prática entre os dois comandos.)

Jonas Schäfer
fonte
2
Obrigado, esta parece ser a diferença mais importante entre os dois.
Sarke
Você poderia citar um recurso que faça backup dessa reivindicação, por favor? Eu ficaria muito interessado em saber como o servidor pode detectar a chamada 'sh'.
Alfred Armstrong
1
@AlfredArmstrong Uhm, coloquei um link na vulnerable to server-side detectionfrase. Isso leva a uma postagem no blog que explica detalhadamente como eles são alcançados. TL; DR: coloque um sono em seu script e observe o atraso na recepção no servidor.
Jonas Schäfer
1
@JonasWielicki obrigado - o link não estava muito claro - não é sua culpa, até o CSS da SE, eu acho. As pessoas são brilhantemente sorrateiras, não são? :)
Alfred Armstrong
1
Tudo isso me leva a pensar se alguém já tentou fazer esse truque na realidade sem ser pego imediatamente. Não que isso provavelmente importe muito, já que você provavelmente pode fazer com que o script faça algo malicioso, mesmo sem esses truques, e qualquer um que não o leia completamente estará vulnerável.
Ilkkachu
11

Eu acredito que eles são praticamente idênticos. No entanto, existem casos raros em que são diferentes.

$(cmd)é substituído pelos resultados de cmd. Se o comprimento desse comando de resultado exceder o valor máximo do tamanho do argumento retornado por getconf ARG_MAX, ele truncará o resultado, o que pode resultar em resultados imprevisíveis.

A opção de tubulação não possui essa limitação. Cada linha de saída do curlcomando será executada bashquando chegar do pipe.

Mas ARG_MAX geralmente está no intervalo de 256.000 caracteres. Para uma instalação do docker, eu ficaria confiante usando qualquer um dos métodos. :-)

Greg Tarsa
fonte
Interessante, então há uma diferença. Obrigado
Sarke
1
Em caso de dúvida, use o método de tubulação. Não especifiquei uma preferência na minha resposta, mas preferiria o método de pipe para esse tipo de uso, porque você nunca sabe quantos dados estão chegando pelo pipe.
Greg Tarsa
2
"truncará o resultado" - O shell deve emitir uma mensagem de erro para isso, não truncar silenciosamente. Ao testar, recebo até um erro do shell bem abaixo ARG_MAX, o bash limita um argumento individual a 131072 bytes no meu sistema, quando é getconf ARG_MAXimpresso 2097152. Mas de qualquer maneira, erro ou truncamento, não funcionaria.
hvd 22/01
Porém , implementações antigas do shell Bourne tinham limites muito mais baixos. No 4.2BSD, o limite era de 10240 caracteres e, em sistemas anteriores, era ainda mais baixo. É claro que isso foi há 30 anos, então é improvável que você encontre limites tão baixos hoje. Se bem me lembro, algumas dessas primeiras conchas simplesmente truncaram silenciosamente.
AndyB
O limite de 128 kB para um único argumento é uma coisa do Linux, não é sobre o Bash.
ilkkachu
8

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

  • Ambos os comandos curle shcomeçarão ao mesmo tempo nos respectivos subshells

  • O STDOUT de curlserá passado como o STDIN para sh(é isso que pipe |, faz)

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

  • A substituição do comando,, $()será executada primeiro, isto é curl, será executada primeiro em um subshell

  • A substituição do comando,, $()será substituída pelo STDOUT decurl

  • sh -c (shell não interativo e sem login) executará o STDOUT de curl

heemail
fonte
1
Então, existe alguma diferença real?
Sarke
@ Sarke Sim, teoricamente, como eu mencionei, mas praticamente praticamente imperceptível. (Não haveria efeito visível se você deixar a substituição de comando não cotadas.)
heemayl
1
@Sarke, se os scripts não forem baixados completamente, você não notaria necessariamente com a tubulação. O processo que está sendo canalizado pode ignorar esse sinal.
Janus Troelsen
@JanusTroelsen, o que você quer dizer com não baixou completamente? Por que isso aconteceria, exceto por um erro no servidor? Nesse caso, não haverá nada para o sh.
hasufell
Ou interrupção de conexão ... existem muitas maneiras pelas quais uma transferência pode falhar
Janus Troelsen
0

Uma diferença entre as duas (extraídas de outras respostas da Web) é que, se você não baixar o script inteiro de uma só vez, ele pode interromper o processo em um ponto desconhecido e alterar o significado do comando. executado. Portanto, parece primeiro fazer o download do arquivo inteiro e depois avaliar se seria melhor.

Lance Pollard
fonte