> & - มีประสิทธิภาพมากกว่า> / dev / null หรือไม่


58

เมื่อวานนี้ผมอ่านนี้เพื่อแสดงความคิดเห็นที่บอกว่าในเปลือก (อย่างน้อยbash) "มีผลเช่นเดียวกับ">&->/dev/null

ความคิดเห็นนั้นหมายถึงคู่มือ ABSเป็นแหล่งข้อมูล แต่แหล่งที่มาบอกว่า>&-ไวยากรณ์ "ปิด file descriptors"

ไม่ชัดเจนสำหรับฉันว่าการกระทำสองอย่างของการปิดไฟล์ descriptor และการเปลี่ยนเส้นทางไปยังอุปกรณ์ null นั้นมีความเทียบเท่าโดยสิ้นเชิง ดังนั้นคำถามของฉันคือพวกเขา?

บนพื้นผิวของมันดูเหมือนว่าการปิด descriptor เป็นเหมือนการปิดประตู แต่การเปลี่ยนเส้นทางไปยังอุปกรณ์ null กำลังเปิดประตูสู่บริเวณขอบรก! ทั้งสองดูเหมือนจะไม่เหมือนกันกับฉันเพราะถ้าฉันเห็นประตูที่ปิดฉันจะไม่พยายามโยนอะไรออกไปจากมัน แต่ถ้าฉันเห็นประตูเปิดฉันจะถือว่าฉันทำได้

ในคำอื่น ๆ ฉันสงสัยเสมอว่า>/dev/nullหมายความว่าcat mybigfile >/dev/nullจริง ๆ แล้วจะประมวลผลทุกไบต์ของไฟล์และเขียนมันเพื่อ/dev/nullที่จะลืมมัน ในทางกลับกันถ้าเชลล์พบ file descriptor ที่ปิดฉันมักจะคิดว่า (แต่ไม่แน่ใจ) ว่ามันจะไม่เขียนอะไรเลยแม้ว่าคำถามcatนั้นจะยังคงอ่านทุกไบต์

ความคิดเห็นนี้พูด>&-และ>/dev/null" ควร " เหมือนกัน แต่มันไม่ได้ดังก้องคำตอบสำหรับฉัน ฉันต้องการคำตอบที่เชื่อถือได้มากขึ้นโดยอ้างอิงถึงแกนมาตรฐานหรือแหล่งข้อมูลบางส่วนหรือไม่ ...


หากฉันต้องการที่จะเป็นกุศลฉันจะบอกว่าความคิดเห็นนั้นไม่ได้หมายความว่าพวกเขาได้รับการดำเนินการในลักษณะเดียวกันพวกเขาทั้งคู่มีผลลัพธ์ที่เหมือนกันในการป้องกันไม่ให้คุณเห็นผลลัพธ์ของโปรแกรม
Barmar

คำตอบ:


71

ไม่แน่นอนคุณไม่ต้องการปิดตัวให้คำอธิบายไฟล์ 0, 1 และ 2

หากคุณทำเช่นนั้นครั้งแรกที่แอปพลิเคชันเปิดไฟล์มันจะกลายเป็น stdin / stdout / stderr ...

ตัวอย่างเช่นหากคุณ:

echo text | tee file >&-

เมื่อtee(อย่างน้อยการใช้งานบางอย่างเช่น busybox ') เปิดไฟล์สำหรับการเขียนมันจะเปิดใน file descriptor 1 (stdout) ดังนั้นteeจะเขียนtextสองครั้งในfile:

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

ที่ได้รับทราบกันดีว่าทำให้เกิดช่องโหว่ด้านความปลอดภัย ตัวอย่างเช่น

chsh 2>&-

และchsh(การประยุกต์ใช้ setuid) /etc/passwdอาจท้ายการเขียนข้อความผิดพลาดใน

เครื่องมือบางอย่างและแม้แต่ห้องสมุดบางแห่งก็พยายามป้องกันสิ่งนั้น ตัวอย่างเช่น GNU teeจะย้ายไฟล์อธิบายไปที่หนึ่งใน 2 ด้านบนหากไฟล์ที่เปิดสำหรับการเขียนได้รับมอบหมาย 0, 1, 2 ในขณะที่ busybox teeจะไม่

เครื่องมือส่วนใหญ่หากพวกเขาไม่สามารถเขียนไปยัง stdout (เพราะเช่นมันไม่ได้เปิด) จะรายงานข้อผิดพลาดใน stderr (ในภาษาของผู้ใช้ซึ่งหมายถึงการประมวลผลพิเศษเพื่อเปิดและแยกไฟล์แปล ... ) ดังนั้น มันจะมีประสิทธิภาพลดลงอย่างมีนัยสำคัญและอาจทำให้โปรแกรมล้มเหลว

ไม่ว่าในกรณีใดมันจะมีประสิทธิภาพมากกว่า โปรแกรมจะทำการwrite()เรียกระบบ มันจะมีประสิทธิภาพมากขึ้นถ้าโปรแกรมยกเลิกการเขียนไปยัง stdout / stderr หลังจากการwrite()เรียกระบบล้มเหลวครั้งแรกแต่โดยทั่วไปโปรแกรมจะไม่ทำเช่นนั้น พวกเขามักจะออกด้วยข้อผิดพลาดหรือพยายามต่อไป


4
ฉันคิดว่าคำตอบนี้จะดียิ่งขึ้นถ้าย่อหน้าสุดท้ายอยู่ด้านบนสุด (เนื่องจากเป็นสิ่งที่ตอบคำถามของ OP ได้โดยตรงที่สุด) และจากนั้นจะหารือกันว่าทำไมจึงเป็นความคิดที่ไม่ดีแม้ว่ามันจะใช้งานได้เป็นส่วนใหญ่ก็ตาม แต่ฉันจะเอามันมี upvote ;)
CVN

@ StéphaneChazelas: อย่างที่ไมเคิลพูดฉันคาดว่าจะมีพาราสุดท้ายอยู่ข้างบน แต่ขอบคุณที่ให้ความกระจ่างว่าจริงๆแล้วมันอาจสร้างปัญหาขึ้นมาเท่านั้น ดังนั้นฉันเดาว่าคำถามเสริมจะเกิดขึ้นเมื่อใดการปิด FD จะมีประโยชน์จริงหรือไม่ หรือฉันควรถามว่าเป็นคำถามแยกต่างหาก
jamadagni

@jamadagni ดูunix.stackexchange.com/search?q=user%3A22565+%223%3E%26-%22สำหรับบางตัวอย่าง
Stéphane Chazelas

1
@jamadagni หากลิงก์ที่จัดหาโดยStéphaneไม่ตอบคำถามฉันจะบอกว่าเสียงเหมือนจุดเริ่มต้นของคำถามแยกต่างหากเนื่องจากไม่เกี่ยวข้องโดยตรงกับประสิทธิภาพเชิงสัมพัทธ์ของทั้งสองวิธี
CVN

1
ฉันซาบซึ้งที่สเตฟานเริ่มต้นด้วยคำเตือนความปลอดภัยที่สำคัญนี้เพราะจะมองเห็นได้น้อยกว่าหากย่อหน้าสุดท้ายอยู่ด้านบน +1 จากฉัน
Olivier Dulac

14

IOW ฉันสงสัยอยู่เสมอว่า>/dev/nullหมายความว่าcat mybigfile >/dev/nullจริง ๆ แล้วจะประมวลผลทุกไบต์ของไฟล์และเขียนไป/dev/nullที่ลืมมัน

ไม่ใช่คำตอบสำหรับคำถามของคุณอย่างเต็มรูปแบบ แต่ใช่ข้างต้นเป็นวิธีการทำงาน

catอ่านไฟล์ที่ระบุชื่อหรืออินพุตมาตรฐานหากไม่มีชื่อไฟล์และเอาต์พุตไปยังเอาต์พุตมาตรฐานของเนื้อหาเหล่านั้นจนกว่าจะพบ EOF on (รวมถึงอินพุตมาตรฐาน) ซึ่งเป็นไฟล์สุดท้ายที่ชื่อ นั่นคืองานของมัน

โดยการเพิ่ม>/dev/nullคุณจะเปลี่ยนเส้นทางออกมาตรฐานไปที่ / dev / null นั่นคือไฟล์พิเศษ (โหนดอุปกรณ์) ซึ่งจะทิ้งสิ่งที่เขียนอยู่ในนั้น (และส่งคืน EOF ทันทีที่อ่าน) โปรดทราบว่าการเปลี่ยนเส้นทางของ I / O เป็นคุณลักษณะที่จัดไว้ให้โดยเปลือกที่ไม่ได้โดยการประยุกต์ใช้ของแต่ละคนและว่ามีอะไรวิเศษเกี่ยวกับชื่อ / dev / null เพียงสิ่งที่เกิดขึ้นจะอยู่ที่นั่นในที่สุดระบบ Unix เหมือน

สิ่งสำคัญคือต้องทราบว่ากลไกเฉพาะของโหนดอุปกรณ์แตกต่างจากระบบปฏิบัติการไปยังระบบปฏิบัติการ แต่ cat (ซึ่งในระบบ GNU หมายถึง coreutils) คือ cross-platform (ซอร์สโค้ดเดียวกันต้องทำงานอย่างน้อยบน Linux และ Hurd) และด้วยเหตุนี้จึงไม่สามารถอ้างอิงได้กับเมล็ดของระบบปฏิบัติการที่เฉพาะเจาะจง นอกจากนี้ยังคงใช้งานได้หากคุณสร้างนามแฝง / dev / null (บน Linux ซึ่งหมายความว่าโหนดอุปกรณ์ที่มีหมายเลขอุปกรณ์หลัก / รองเดียวกัน) ด้วยชื่ออื่น และมักมีกรณีของการเขียนที่อื่นซึ่งทำงานเหมือนกันอย่างมีประสิทธิภาพ (พูด / dev / ศูนย์)

มันตามมาโดยcatไม่ทราบคุณสมบัติพิเศษของ / dev / null และแน่นอนว่าไม่รู้จักการเปลี่ยนเส้นทางในตอนแรก แต่ก็ยังต้องทำงานเหมือนเดิม:อ่านไฟล์ที่มีชื่อและส่งออกเนื้อหาของ ไฟล์ / เหล่านั้นไปยังเอาต์พุตมาตรฐาน การที่เอาท์พุทมาตรฐานของการcatเกิดเป็นโมฆะไม่ใช่สิ่งที่catตัวเองกังวล


2
หากต้องการขยายคำตอบของคุณ: ใช่cat mybigfile > /dev/nullจะทำให้catอ่านทุกไบต์ของbigfileหน่วยความจำ และสำหรับทุกไบต์มันจะเรียกn write(1, buffer, n)ไม่ทราบcatโปรแกรมwriteจะไม่ทำอะไรเลยอย่างแน่นอน (ยกเว้นการทำบัญชีเล็กน้อย) การเขียนไป/dev/nullยังไม่จำเป็นต้องประมวลผลทุกไบต์
G-Man

2
ฉันจำได้ว่าฉันปลิวไปเมื่อฉันอ่าน Linux kernel ที่มาของอุปกรณ์ / dev / null ฉันคาดหวังว่าจะมีระบบที่ซับซ้อนของฟรี () ไอเอ็นจีบัฟเฟอร์ ฯลฯ แต่ไม่มีมันเป็นเพียงผลตอบแทน ()
Brian Minton

4
@ G-Man: ฉันไม่รู้ว่าคุณสามารถรับประกันได้ว่าจะเป็นจริงในทุกกรณี ฉันไม่สามารถหาหลักฐานได้ในตอนนี้ แต่ฉันจำได้ว่าการนำไปใช้งานอย่างใดอย่างหนึ่งcatหรือcpว่าจะทำงานได้โดยmmapการโหลดไฟล์ขนาดใหญ่ลงในหน่วยความจำจากนั้นจึงเรียกwrite()ใช้พื้นที่แมป หากคุณเขียนถึง/dev/nullการwrite()โทรจะส่งคืนพร้อมกันโดยไม่มีข้อผิดพลาดในหน้าของไฟล์ต้นฉบับดังนั้นจึงไม่สามารถอ่านจากดิสก์ได้
Nate Eldredge

2
นอกจากนี้บางอย่างเช่น GNU catทำงานบนหลาย ๆ แพลตฟอร์ม แต่การมองอย่างรวดเร็วที่ซอร์สโค้ดจะแสดงมากมาย#ifdef: มันไม่ใช่รหัสเดียวกันที่ทำงานบนแพลตฟอร์มทั้งหมดและมีส่วนที่ขึ้นกับระบบมากมาย
Nate Eldredge

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