Qual è la differenza tra `curl | sh` e `sh -c“ $ (curl) ”`?


23

Un metodo di installazione semplice per Docker (ad esempio) è questo:

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

Tuttavia, ne ho anche visti alcuni che assomigliano a questo (usando l'esempio Docker):

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

Sembrano essere funzionalmente uguali, ma c'è un motivo per usarne uno rispetto all'altro? O è solo una cosa preferenziale / estetica?

(Da notare, fai molta attenzione quando esegui script di origini sconosciute.)

Risposte:


39

C'è una differenza pratica.

curl -sSL https://get.docker.com/ | shinizia curle shcontemporaneamente collega l'output di curlcon l'input di sh. curleseguirà il download (all'incirca) il più velocemente shpossibile per eseguire lo script. Il server può rilevare le irregolarità nei tempi e iniettare codice dannoso non visibile quando si scarica semplicemente la risorsa in un file o buffer o quando si visualizza in un browser.

In sh -c "$(curl -sSL https://get.docker.com/)", curlviene eseguito rigorosamente prima di shessere eseguito. L'intero contenuto della risorsa viene scaricato e passato alla shell prima shdell'avvio. La shell inizia solo shquando curlè terminata e gli passa il testo della risorsa. Il server non è in grado di rilevare la shchiamata; viene avviato solo al termine della connessione. È simile a scaricare prima lo script in un file.

(Questo potrebbe non essere rilevante nel caso della finestra mobile, ma può essere un problema in generale e mette in evidenza una differenza pratica tra i due comandi.)


2
Grazie, questa sembra la differenza più importante tra i due.
Sarke,

Potresti citare una risorsa a sostegno di questa affermazione, per favore? Sarei molto interessato a sapere come il server può rilevare la chiamata 'sh'.
Alfred Armstrong,

1
@AlfredArmstrong Uhm, ho messo un link sulla vulnerable to server-side detectionfrase. Conduce a un post sul blog che spiega dettagliatamente come lo raggiungono. TL; DR: sospendi lo script e osserva il ritardo nella ricezione sul server.
Jonas Schäfer,

1
@JonasWielicki grazie - il collegamento non è stato molto chiaro - non è colpa tua, penso al CSS di SE. Le persone sono brillantemente subdole, vero? :)
Alfred Armstrong,

1
Tutto ciò mi porta a chiedermi se qualcuno abbia mai provato a fare quel trucco nella realtà senza essere immediatamente catturato. Non che probabilmente importi molto, dal momento che probabilmente potresti semplicemente fare in modo che lo script faccia qualcosa di dannoso anche senza tali trucchi, e chiunque non lo legga per intero sarebbe vulnerabile.
ilkkachu,

11

Credo che siano praticamente identici. Tuttavia, ci sono rari casi in cui sono diversi.

$(cmd)viene sostituito con i risultati di cmd. Se la lunghezza di quel comando di risultato supera il valore della lunghezza massima dell'argomento restituito da getconf ARG_MAX, troncerà il risultato, il che potrebbe dare risultati imprevedibili.

L'opzione pipe non presenta questa limitazione. Ogni riga di output dal curlcomando verrà eseguita bashquando arriva dalla pipe.

Ma ARG_MAX è generalmente nell'intervallo di 256.000 caratteri. Per un'installazione docker, sarei sicuro di usare entrambi i metodi. :-)


Interessante, quindi c'è una differenza. Grazie
Sarke,

1
In caso di dubbio, utilizzare il metodo pipe. Non ho specificato una preferenza nella mia risposta, ma preferirei il metodo pipe per questo tipo di utilizzo perché non si sa mai quanti dati arrivano attraverso la pipe.
Greg Tarsa,

2
"troncerà il risultato" - La shell dovrebbe emettere un messaggio di errore per quello, non troncare silenziosamente. Durante il test, ricevo anche un errore dalla shell molto più in basso ARG_MAX, bash limita un singolo argomento a 131072 byte sul mio sistema, quando getconf ARG_MAXstampa 2097152. In ogni caso, errore o troncamento, non funzionerebbe.
hvd,

Ma le vecchie implementazioni della shell Bourne avevano limiti molto più bassi. In 4.2BSD il limite era di 10240 caratteri, e sui sistemi precedenti era ancora più basso .. Certo che lo era 30 anni fa, quindi è improbabile che incontriate limiti così bassi oggi. Se ricordo bene, alcune di queste prime conchiglie si sono troncate silenziosamente.
AndyB,

Il limite di 128 kB per un singolo argomento è una cosa di Linux, non riguarda Bash.
ilkkachu,

8

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

  • Entrambi i comandi curle sh, inizieranno contemporaneamente, nei rispettivi sottotitoli

  • Lo STDOUT da curlverrà passato come STDIN a sh(questo è ciò che pipe |, fa)

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

  • La sostituzione del comando $(), verrà eseguita per prima, ovvero curlverrà eseguita per prima in una subshell

  • La sostituzione del comando,, $()sarà sostituita da STDOUT dacurl

  • sh -c (shell non interattiva, non login) eseguirà lo STDOUT da curl


1
Quindi, c'è qualche vera differenza?
Sarke,

@Sarke Sì, teoricamente come ho già detto, ma praticamente non si nota. (Ci sarebbero effetti visibili se lasci la sostituzione del comando senza
citazione

1
@Sarke, se gli script non sono stati scaricati completamente, non lo noteresti necessariamente con piping. Il processo a cui viene eseguito il piping può ignorare quel segnale.
Janus Troelsen,

@JanusTroelsen cosa intendi con non scaricato completamente? Perché dovrebbe accadere se non per un errore del server, nel qual caso non ci sarà nulla inoltrato a sh.
Hasufell

O l'interruzione della connessione ... ci sono molti modi in cui un trasferimento può fallire
Janus Troelsen l'

0

Una differenza tra i due (presa da altre risposte sul Web) è che se non scarichi l'intero script in una sola volta, potrebbe tagliare a metà dello script in un punto sconosciuto e cambiare il significato del comando eseguito. Quindi sembra prima scaricare l'intero file e quindi valutarlo sarebbe meglio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.