ความแตกต่างระหว่าง STDIN และอาร์กิวเมนต์ที่ส่งผ่านไปยังคำสั่งคืออะไร


16

ฉันสามารถใช้ทั้งสองรูปแบบเพื่อดำเนินการcatวิธีการ:

cat file_name
cat < file_name

ผลลัพธ์จะเหมือนกัน

จากนั้นฉันต้องการที่จะดำเนินการmanในรูปแบบของstdin

man < file_name

ในขณะที่file_nameมี:

# file_name
cat

แต่ก็ปรากฏขึ้น แทนการดำเนินการWhat manual page do you want?man cat

ฉันต้องการทราบว่าทำไมcatสามารถยอมรับstdinเป็นข้อโต้แย้ง แต่manไม่สามารถ และสิ่งที่แตกต่างระหว่างอาร์กิวเมนต์บรรทัดคำสั่งและstdin?

คำตอบ:


21

คำถามของคุณเกี่ยวข้องอย่างใกล้ชิดกับวิธีที่เชลล์ที่คุณใช้วิเคราะห์คำที่ผู้ใช้ป้อนในบรรทัดคำสั่ง

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

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

ในกรณีที่คุณเพิ่มอักขระพิเศษ<หรือ>ในบรรทัดคำสั่งของคุณเชลล์จะไม่ผนวก<และ>ไม่ใช้คำที่ตามมาในอาร์เรย์ที่จะถูกส่งผ่านไปยังโปรแกรม ด้วย<หรือ>ให้เปลือกเริ่มทำสิ่งแฟนซีสนับสนุนโดยเคอร์เนลพื้นฐาน ( ท่อคำหลัก) เพื่อให้เข้าใจว่าเกิดอะไรขึ้นคุณต้องเข้าใจว่าอะไรSTDINและSTDOUT(เนื่องจากไม่เกี่ยวข้องทันทีที่ฉันละเว้นSTDERR) คืออะไร

ทุกอย่างที่คุณเห็นในเทอร์มินัลของคุณ (ในกรณีส่วนใหญ่ของจอแสดงผลของคุณ) เขียนโดยเชลล์หรือโปรแกรมอื่น ๆ ที่คุณเรียกใช้ก่อนหน้านี้ไปยังไฟล์พิเศษ (ในยูนิกซ์ทุกอย่างเป็นไฟล์ ) STDOUTไฟล์นี้มีรหัสพิเศษและถูกเรียกว่า หากโปรแกรมต้องการที่จะอ่านข้อมูลจากแป้นพิมพ์มัน dosn't สำรวจแป้นพิมพ์โดยตรง (อย่างน้อยในกรณีส่วนใหญ่) STDINแต่อ่านจากแฟ้มพิเศษที่เรียกว่า ภายในไฟล์นี้เชื่อมต่อกับอุปกรณ์อินพุตมาตรฐานแป้นพิมพ์ของคุณในกรณีส่วนใหญ่

หากเชลล์อ่าน<หรือ>ในบรรทัดคำสั่งที่แยกวิเคราะห์มันจะจัดการSTDINหรือSTDOUTในชนิดเฉพาะสำหรับเวลาที่โปรแกรมที่เกี่ยวข้องกำลังทำงานอยู่ STDINและไม่ได้STDOUTชี้ไปที่เทอร์มินัลหรืออุปกรณ์อินพุตมาตรฐานอีกต่อไป แต่จะไปที่ชื่อไฟล์ที่ตามมาในบรรทัดคำสั่ง

ในกรณีของสองบรรทัด

cat file_name
cat < file_name

พฤติกรรมที่สังเกตได้นั้นเหมือนกันเพราะผู้พัฒนาที่เกี่ยวข้องนั้นทำการcatอ่านข้อมูลจากSTDINหรืออ่านข้อมูลจากไฟล์ซึ่งมีชื่อที่ได้รับเป็นอาร์กิวเมนต์บรรทัดคำสั่งแรก (ซึ่งเป็นองค์ประกอบแรกในอาร์เรย์ที่เชลล์ส่งไปcat) ต่อมาcatเขียนเนื้อหาทั้งหมดของfile_nameหรือไปยังสถานีเนื่องจากเราไม่ได้สั่งให้เปลือกในการจัดการSTDIN STDOUTจำไว้ว่าในบรรทัดที่สองเชลล์ของคุณใช้STDINวิธีนี้มันไม่ได้ชี้ไปที่อุปกรณ์อินพุตมาตรฐานของคุณอีกต่อไป แต่ชี้ไปที่ไฟล์ที่เรียกว่าfile_nameในไดเรกทอรีการทำงานปัจจุบันของคุณ

ในกรณีอื่น ๆ ของสาย

man < file_name

manไม่ได้หมายถึงการอ่านอะไรจากSTDINถ้ามันถูกเรียกโดยไม่มีข้อโต้แย้งเช่นอาร์เรย์ที่ว่างเปล่า ดังนั้นสาย

man < file_name

เท่ากับ

man

ยกตัวอย่างเช่นmanจะอ่านอะไรบางอย่างจากSTDIN, เกินไปถ้าคุณผ่านไป-l - manด้วยตัวเลือกนี้ที่ให้ไว้ในบรรทัดคำสั่งคุณสามารถแสดงเนื้อหาของสิ่งที่manอ่านจากSTDINใน terminal ของคุณ ดังนั้น

man -l - < file_name

จะทำงานได้ด้วย (แต่โปรดระวังmanไม่ใช่เพียงเพจเจอร์ แต่ยังแยกวิเคราะห์อินพุตของไฟล์และดังนั้นเนื้อหาไฟล์และเนื้อหาที่แสดงอาจแตกต่างกัน)

ดังนั้นวิธีการSTDIN, STDOUTและการขัดแย้งบรรทัดคำสั่งจะถูกตีความขึ้นทั้งหมดให้กับนักพัฒนาที่สอดคล้องกัน

ฉันหวังว่าคำตอบของฉันจะทำให้สิ่งต่าง ๆ ชัดเจนขึ้น


ขอบคุณสำหรับคำอธิบายโดยละเอียด สำหรับย่อหน้าสุดท้ายของคุณคุณกล่าวถึงการใช้man -l - < file_nameการmanตีความSTDINเป็นอาร์กิวเมนต์ แต่มันล้มเหลวในระบบของฉันด้วยSTDERR:man -l - < tee man: invalid option -- l man, version 1.6c
สตีฟ

ไม่เป็นไร แต่ผมไม่ได้พูดถึงว่าอย่างน้อยรุ่นของฉันman( มนุษย์ DB ) อ่านข้อโต้แย้งจากSTDINที่มีการขัดแย้งที่กำหนดตาม-l -มันเป็นเพียงการตีความข้อมูลจากSTDINเป็นหน้าคน สำหรับคำอธิบายโดยละเอียดของอาร์กิวเมนต์ที่ถูกต้องและวิธีการตีความคุณต้องปรึกษา man page ของโปรแกรมที่เกี่ยวข้อง man manในกรณีของคุณให้คำปรึกษา manบางทีนั่นอาจจะเป็นตัวเลือกที่คล้ายกันสำหรับคุณ หากคุณต้องการอ่านอาร์กิวเมนต์บรรทัดคำสั่งสำหรับโปรแกรมเฉพาะจากSTDIN xargs(เช่นที่กล่าวถึงข้างต้น) เป็นวิธีที่จะไป
user1146332

ฉันman manและค้นหาสิ่งที่อยู่ในระบบปฏิบัติการของฉันไม่รองรับ อย่างไรก็ตามขอขอบคุณสำหรับการประกาศแนวคิดสองประการนี้สำหรับฉัน
สตีฟยาง

ฉันแก้ไขคำตอบของคุณแล้วมันมีลิงก์ที่มีประโยชน์จริง ๆ แทนที่จะเป็น lmgtfy โพสต์ลิงก์ lmgtfy คือ 1) หยาบคาย, 2) ไม่ช่วยเหลือและ 3) ขมวดคิ้วจริงๆในเว็บไซต์ SE ให้ลิงค์หรือไม่ทำก็ได้ แต่ถ้าคุณเลือกที่จะทำเช่นนั้นให้ลิงค์ไปยังข้อมูลจริงและไม่แสดงวิธีการเยาะเย้ยใครสักคน
terdon

12

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

ดูเหมือนว่าคุณคาดว่าmanจะอ่าน stdin เพื่อหาหน้าคนที่ควรจะแสดงซึ่งจะเป็นพฤติกรรมที่แปลกจริงๆ คุณจะใช้เมื่อไหร่ ความจริงที่catแสดง stdin ของมันเป็นสิ่งประดิษฐ์ของความจริงที่ว่ามันไม่ทำอะไรเลย ฉันไม่คิดว่าเครื่องมืออื่นใดจะทำงานอย่างนั้น ตัวอย่างเช่นgrepสามารถใช้ชื่อไฟล์หรืออ่านจากstdinแต่มันประมวลผลข้อมูลบนstdinมันไม่ได้อ่านชื่อไฟล์จากstdinนั้นเปิด

หากคุณต้องการพฤติกรรมนี้จริงๆคุณสามารถใช้xargsซึ่งจะแปลงไฟล์เป็นอาร์กิวเมนต์บรรทัดคำสั่ง:

$ xargs man < file_name

หรือเพียงแค่รับcatสายภายในการmanโทร:

$ man $(cat file_name)

man $(<file_name)ในทุบตีคุณสามารถใช้
จอร์แดน

Re: "ผมไม่คิดว่าเครื่องมืออื่น ๆ วิธีการทำงานนั้น" - ของ Perl (<>)ในวงจะทำSTDINหรืออาร์กิวเมนต์บรรทัดคำสั่งเป็นชื่อไฟล์ ...
แอรอน D. Marasco

@ AaronD.Marasco ฉันหมายถึงไม่มีเครื่องมือที่จะโต้แย้งหรืออ่านชื่อไฟล์จาก stdin และอ่านการโต้เถียงจากไฟล์นั้น
Michael Mrozek

@MichaelMrozek ขอบคุณสำหรับคำชี้แจงนี้ จุดประสงค์ที่ฉันใช้man < file_nameคือเพื่อช่วยให้ตัวเองเข้าใจแนวคิดทั้งสองนี้ เมื่ออ่านคำอธิบายของคุณแล้วการดำเนินการจะตัดสินโดยผู้เขียนคำสั่ง ดังนั้นถ้าฉันอยู่ภายใต้การfindโต้แย้งจะใช้เวลาค่อนข้างประมวลผล STDIN?
สตีฟยาง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.