ความแตกต่างระหว่าง `curl | sh` และ `sh -c“ $ (curl)””?


23

วิธีการติดตั้งง่าย ๆ อย่างหนึ่งสำหรับ Docker (ตัวอย่าง) คือ:

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

อย่างไรก็ตามฉันได้เห็นบางอย่างที่มีลักษณะเช่นนี้ (โดยใช้ตัวอย่าง Docker):

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

พวกมันดูเหมือนจะใช้งานได้เหมือนกัน แต่มีเหตุผลที่จะใช้อันหนึ่งกับอีกอันหนึ่งหรือไม่? หรือมันเป็นเพียงสิ่งที่ชอบ / ความงาม?

(โปรดทราบว่าให้ระมัดระวังเมื่อเรียกใช้สคริปต์จากต้นกำเนิดที่ไม่รู้จัก)

คำตอบ:


39

มีความแตกต่างในทางปฏิบัติ

curl -sSL https://get.docker.com/ | shเริ่มต้นcurlและshในเวลาเดียวกันการเชื่อมต่อการส่งออกของด้วยการป้อนข้อมูลของcurl จะดำเนินการพร้อมกับการดาวน์โหลด (คร่าวๆ) เร็วเท่าที่จะเรียกใช้สคริปต์ได้ เซิร์ฟเวอร์สามารถตรวจจับความผิดปกติในช่วงเวลาและฉีดโค้ดอันตรายที่มองไม่เห็นเมื่อเพียงดาวน์โหลดทรัพยากรลงในไฟล์หรือบัฟเฟอร์หรือเมื่อดูในเบราว์เซอร์shcurlsh

ในsh -c "$(curl -sSL https://get.docker.com/)", curlจะดำเนินการอย่างเคร่งครัดก่อนที่จะshมีการเรียก เนื้อหาทั้งหมดของทรัพยากรจะถูกดาวน์โหลดและส่งไปยังเชลล์ของคุณก่อนที่shจะเริ่ม เชลล์ของคุณเริ่มต้นshเมื่อcurlออกแล้วและส่งผ่านข้อความของทรัพยากรไปยังมัน เซิร์ฟเวอร์ไม่สามารถตรวจพบการshโทร มันจะเริ่มต้นหลังจากการเชื่อมต่อสิ้นสุดลง มันคล้ายกับการดาวน์โหลดสคริปต์ลงในไฟล์ก่อน

(สิ่งนี้อาจไม่เกี่ยวข้องในกรณีนักเทียบท่า แต่อาจเป็นปัญหาโดยทั่วไปและเน้นถึงความแตกต่างระหว่างสองคำสั่ง)


2
ขอบคุณดูเหมือนว่าความแตกต่างที่สำคัญที่สุดระหว่างสองคนนี้
Sarke

คุณสามารถอ้างอิงทรัพยากรสำรองข้อเรียกร้องนี้ได้ไหม ฉันสนใจที่จะทราบว่าเซิร์ฟเวอร์สามารถตรวจจับการโทร 'sh' ได้อย่างไร
Alfred Armstrong

1
@AlfredArmstrong Uhm ฉันใส่ลิงค์บนvulnerable to server-side detectionวลี มันนำไปสู่การโพสต์บล็อกที่อธิบายในรายละเอียดที่ดีว่าพวกเขาบรรลุมัน TL; DR: พักในสคริปต์ของคุณและสังเกตการหน่วงเวลาในการรับบนเซิร์ฟเวอร์
Jonas Schäfer

1
@ JonasWielicki ขอบคุณ - ลิงค์ไม่ชัดเจน - ไม่ใช่ความผิดของคุณลงไปที่ CSS ของ SE ฉันคิดว่า ผู้คนต่างลับ ๆ ล่อๆอย่างชาญฉลาดใช่ไหม :)
อัลเฟรดอาร์มสตรอง

1
ทั้งหมดนี้ทำให้ฉันสงสัยว่าใครเคยลองทำเคล็ดลับในความเป็นจริงโดยไม่ถูกจับทันที ไม่ใช่ว่ามันอาจจะมีความสำคัญมากเพราะคุณอาจแค่ให้สคริปต์ทำบางสิ่งที่เป็นอันตรายถึงแม้จะไม่มีลูกเล่นและใครก็ตามที่ไม่ได้อ่านอย่างเต็มที่จะมีความเสี่ยง
ilkkachu

11

ฉันเชื่อว่าพวกเขาเหมือนกันจริง อย่างไรก็ตามมีบางกรณีที่พบได้ยากซึ่งแตกต่างกัน

$(cmd)cmdได้รับการแทนที่ด้วยผลของการ หากความยาวของคำสั่งผลลัพธ์นั้นเกินค่าความยาวอาร์กิวเมนต์สูงสุดที่ส่งคืนโดยgetconf ARG_MAXจะตัดผลลัพธ์ซึ่งอาจส่งผลให้ผลลัพธ์ไม่สามารถคาดเดาได้

ตัวเลือกไปป์ไม่มีข้อ จำกัด นี้ แต่ละบรรทัดของเอาต์พุตจากcurlคำสั่งจะถูกดำเนินการโดยbashเมื่อมาจากไพพ์

แต่ ARG_MAX มักจะอยู่ในช่วง 256,000 ตัวอักษร สำหรับการติดตั้งตัวติดตั้งฉันมั่นใจว่าใช้วิธีใดวิธีหนึ่ง :-)


น่าสนใจดังนั้นจึงมีความแตกต่าง ขอบคุณ
Sarke

1
หากมีข้อสงสัยให้ใช้วิธีการไพพ์ ฉันไม่ได้ระบุการตั้งค่าในคำตอบของฉัน แต่ฉันต้องการวิธีการไพพ์สำหรับการใช้งานประเภทนี้เพราะคุณไม่เคยรู้ว่าข้อมูลผ่านมาทางไพพ์มากแค่ไหน
Greg Tarsa

2
"มันจะตัดทอนผลลัพธ์" - เชลล์ควรออกข้อความแสดงข้อผิดพลาดสำหรับสิ่งนั้นไม่ใช่ตัดทอนอย่างเงียบ ๆ เมื่อการทดสอบผมได้รับข้อผิดพลาดจากเปลือกต่ำกว่าARG_MAXทุบตี จำกัด อาร์กิวเมนต์แต่ละ 131,072 ไบต์ในระบบของฉันเมื่อพิมพ์getconf ARG_MAX 2097152แต่ไม่ว่าจะด้วยวิธีใดข้อผิดพลาดหรือการตัดทอนก็ไม่ได้ผล
hvd

แต่การนำไปใช้งานเก่าของ Bourne shell นั้นมีขีด จำกัด ที่ต่ำกว่ามาก ใน 4.2BSD นั้นมีขีด จำกัด อยู่ที่ 10240 ตัวอักษรและในระบบก่อนหน้านี้มันก็ต่ำลง .. แน่นอนว่าเมื่อ 30 ปีที่แล้วดังนั้นคุณจึงไม่น่าจะเจอขีด จำกัด ต่ำในวันนี้ ถ้าฉันจำได้อย่างถูกต้องเปลือกต้นบางส่วนเหล่านี้ไม่ได้ถูกตัดอย่างเงียบ ๆ
AndyB

ขีด จำกัด 128 kB สำหรับอาร์กิวเมนต์เดี่ยวคือสิ่งที่ Linux ไม่เกี่ยวกับ Bash
ilkkachu

8

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

  • ทั้งสองคำสั่งcurlและshจะเริ่มในเวลาเดียวกันใน subshells ที่เกี่ยวข้อง

  • STDOUT จากcurlจะถูกส่งผ่านเป็น STDIN ไปยังsh(นี่คือสิ่งที่ท่อ|ทำ)

ในขณะที่sh -c "$(curl -sSL https://get.docker.com/)":

  • การทดแทนคำสั่ง$()จะถูกดำเนินการก่อนเช่นcurlจะถูกเรียกใช้ครั้งแรกใน subshell

  • การทดแทนคำสั่ง$()จะถูกแทนที่ด้วย STDOUT จากcurl

  • sh -c (เชลล์แบบไม่โต้ตอบและไม่ใช่ล็อกอิน) จะดำเนินการ STDOUT จาก curl


1
ดังนั้นมีความแตกต่างที่แท้จริงหรือไม่?
Sarke

@Sarke ใช่ในทางทฤษฎีอย่างที่ฉันพูดถึง แต่แทบจะไม่สังเกตเห็นได้ชัด (จะมีเอฟเฟ็กต์ที่มองเห็นได้ถ้าคุณปล่อยให้การแทนที่คำสั่งไม่ถูก
อ้างถึง

1
@Sarke หากสคริปต์ไม่ได้ดาวน์โหลดอย่างสมบูรณ์คุณไม่จำเป็นต้องสังเกตว่ามันมีการวางท่อ กระบวนการที่ได้รับ piped สามารถละเว้นสัญญาณนั้นได้
Janus Troelsen

@JanusTroelsen คุณหมายความว่าอย่างไรหากดาวน์โหลดไม่สมบูรณ์ เหตุใดจึงเกิดขึ้นยกเว้นข้อผิดพลาดของเซิร์ฟเวอร์ซึ่งในกรณีนี้จะไม่มีสิ่งใดถูกส่งไปยัง sh
hasufell

หรือการเชื่อมต่อหยุดชะงัก ... มีหลายวิธีที่การถ่ายโอนอาจล้มเหลวได้
Janus Troelsen

0

ความแตกต่างอย่างหนึ่งระหว่างสอง (นำมาจากคำตอบอื่น ๆ ทั่วทั้งเว็บ) คือถ้าคุณไม่ดาวน์โหลดสคริปต์ทั้งหมดในครั้งเดียวมันอาจตัดผ่านสคริปต์ครึ่งทางที่จุดที่ไม่รู้จักและเปลี่ยนความหมายของคำสั่งให้เป็น ดำเนินการ ดังนั้นดูเหมือนว่าก่อนดาวน์โหลดไฟล์ทั้งหมดแล้วประเมินว่ามันจะดีกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.