Apa perbedaan antara `curl | sh` dan `sh -c“ $ (curl) ”`?

23

Salah satu metode instal mudah untuk Docker (misalnya) adalah ini:

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

Namun, saya juga melihat beberapa yang terlihat seperti ini (menggunakan contoh Docker):

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

Secara fungsional mereka tampak sama, tetapi apakah ada alasan untuk menggunakan salah satunya? Ataukah hanya preferensi / estetika?

(Sebagai catatan, berhati-hatilah saat menjalankan skrip dari asal yang tidak diketahui.)

Sarke
sumber

Jawaban:

39

Ada perbedaan praktis.

curl -sSL https://get.docker.com/ | shdimulai curldan shpada saat yang sama, menghubungkan output curldengan input sh. curlakan melakukan pengunduhan (kurang-lebih) secepat shdapat menjalankan skrip. Server dapat mendeteksi penyimpangan dalam pengaturan waktu dan menyuntikkan kode berbahaya yang tidak terlihat saat hanya mengunduh sumber daya ke dalam file atau buffer atau saat melihatnya di browser.

Di sh -c "$(curl -sSL https://get.docker.com/)", curldijalankan dengan ketat sebelum shdijalankan. Seluruh konten sumber daya diunduh dan diteruskan ke shell Anda sebelum shdimulai. Shell Anda hanya dimulai shketika curltelah keluar, dan meneruskan teks sumber daya ke sana. Server tidak dapat mendeteksi shpanggilan; itu hanya dimulai setelah koneksi berakhir. Ini mirip dengan mengunduh skrip ke dalam file terlebih dahulu.

(Ini mungkin tidak relevan dalam kasus buruh pelabuhan, tetapi mungkin masalah secara umum dan menyoroti perbedaan praktis antara kedua perintah.)

Jonas Schäfer
sumber
2
Terima kasih, ini sepertinya perbedaan paling penting di antara keduanya.
Sarke
Bisakah Anda mengutip sumber daya yang mendukung klaim ini? Saya akan sangat tertarik mengetahui bagaimana server dapat mendeteksi panggilan 'sh'.
Alfred Armstrong
1
@AlfredArmstrong Uhm, saya menaruh tautan pada vulnerable to server-side detectionfrasa. Ini mengarah ke posting blog yang menjelaskan dengan sangat rinci bagaimana mereka mencapainya. TL; DR: tidurlah di skrip Anda dan amati keterlambatan penerimaan di server.
Jonas Schäfer
1
@JonasWielicki terima kasih - tautannya tidak terlalu jelas - bukan salah Anda, sampai ke CSS SE saya kira. Orang-orang sangat licik, bukan? :)
Alfred Armstrong
1
Semua itu membuat saya bertanya-tanya apakah ada orang yang pernah mencoba melakukan trik itu dalam kenyataan tanpa segera ketahuan. Bukan berarti itu penting karena Anda mungkin bisa membuat skrip melakukan sesuatu yang berbahaya bahkan tanpa trik seperti itu, dan siapa pun yang tidak membacanya secara penuh akan rentan.
ilkkachu
11

Saya percaya bahwa mereka praktis identik. Namun, ada beberapa kasus langka di mana mereka berbeda.

$(cmd)diganti dengan hasil cmd. Jika panjang perintah hasil melebihi nilai panjang argumen maksimum yang dikembalikan oleh getconf ARG_MAX, itu akan memotong hasilnya, yang dapat menghasilkan hasil yang tidak dapat diprediksi.

Opsi pipa tidak memiliki batasan ini. Setiap garis output dari curlperintah akan dieksekusi oleh bashketika tiba dari pipa.

Tetapi ARG_MAX biasanya dalam kisaran 256.000 karakter. Untuk pemasangan buruh pelabuhan, saya akan percaya diri menggunakan kedua metode ini. :-)

Greg Tarsa
sumber
Menarik, jadi ada perbedaan. Terima kasih
Sarke
1
Jika ragu, gunakan metode pipa. Saya tidak menentukan preferensi dalam jawaban saya, tetapi saya lebih suka metode pipa untuk jenis penggunaan ini karena Anda tidak pernah tahu berapa banyak data yang datang melalui pipa.
Greg Tarsa
2
"itu akan memotong hasilnya" - Shell harus mengeluarkan pesan kesalahan untuk itu, tidak memotong secara diam-diam. Saat menguji, saya bahkan mendapatkan kesalahan dari shell jauh di bawah ARG_MAX, bash membatasi argumen individu ke 131072 byte pada sistem saya, ketika getconf ARG_MAXdicetak 2097152. Tapi bagaimanapun juga, kesalahan atau pemotongan, itu tidak akan berhasil.
hvd
Tetapi implementasi lama dari shell Bourne memiliki batas yang jauh lebih rendah. Dalam 4.2BSD batasnya adalah 10.240 karakter, dan pada sistem sebelumnya itu bahkan lebih rendah .. Tentu saja itu 30 tahun yang lalu, jadi Anda tidak mungkin menemukan batas rendah hari ini. Jika saya ingat dengan benar, beberapa dari kerang-kerang awal ini hanya diam terpotong.
AndyB
Batas 128 kB untuk satu argumen adalah masalah Linux, ini bukan tentang Bash.
ilkkachu
8

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

  • Kedua perintah, curldan sh, akan mulai pada saat yang sama, di masing-masing subkulit

  • STDOUT dari curlakan diteruskan sebagai STDIN untuk sh(ini adalah apa pipa |, lakukan)

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

  • Substitusi perintah $(),, akan dieksekusi pertama yaitu curldijalankan pertama kali dalam sebuah subkulit

  • Substitusi perintah $(),, akan digantikan oleh STDOUT daricurl

  • sh -c (non-interaktif, shell non-login) akan menjalankan STDOUT dari curl

heemayl
sumber
1
Jadi, apakah ada perbedaan nyata?
Sarke
@Arke Ya, secara teoritis seperti yang saya sebutkan, tetapi praktis hampir tidak terlihat. (Akan ada efek yang terlihat jika Anda membiarkan perintah substitusi tidak
dikutip
1
@Arke, jika skrip tidak diunduh sepenuhnya, Anda tidak perlu memperhatikannya dengan perpipaan. Proses yang disalurkan ke dapat mengabaikan sinyal itu.
Janus Troelsen
@ JanusTroelsen apa maksud Anda dengan tidak mengunduh sepenuhnya? Mengapa itu terjadi kecuali untuk kesalahan server dalam hal ini tidak akan ada sesuatu yang disalurkan ke sh.
hasufell
Atau gangguan koneksi ... ada banyak cara transfer gagal
Janus Troelsen
0

Salah satu perbedaan antara keduanya (diambil dari jawaban lain di web) adalah bahwa jika Anda tidak mengunduh seluruh skrip sekaligus, itu bisa memotong setengah jalan melalui skrip pada titik yang tidak diketahui dan mengubah arti perintah menjadi dieksekusi. Jadi sepertinya mengunduh seluruh file terlebih dahulu dan kemudian mengevaluasinya akan lebih baik.

Lance Pollard
sumber