ใช้แมวอย่างไร้ประโยชน์?


101

นี่อาจเป็นคำถามที่พบบ่อยหลาย ๆ คำถาม - แทนที่จะใช้:

cat file | command

(ซึ่งเรียกว่าการใช้แมวอย่างไร้ประโยชน์) วิธีที่ถูกต้องควรจะเป็น:

command < file

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

  1. สวยงามมากขึ้น - ฉันชอบเมื่อข้อมูลเคลื่อนจากซ้ายไปขวาอย่างสม่ำเสมอเท่านั้น และง่ายต่อการเปลี่ยนcatกับสิ่งอื่น ( gzcat, echo... ), เพิ่มไฟล์ที่ 2 หรือใส่กรองใหม่ ( pv, mbuffer, grep... )

  2. ฉัน "รู้สึก" ว่ามันอาจเร็วกว่าในบางกรณี เร็วขึ้นเนื่องจากมี 2 กระบวนการขั้นตอนที่ 1 ( cat) ทำการอ่านและกระบวนการที่สองทำอะไรก็ได้ และสามารถทำงานแบบขนานซึ่งหมายความว่าบางครั้งการดำเนินการเร็วขึ้น

ตรรกะของฉันถูกต้องหรือไม่ (ด้วยเหตุผลที่ 2)


22
catเป็นท่อตัวตน เพียงสตรีมอินพุตไปยังเอาต์พุตเท่านั้น หากโปรแกรมที่สองในห่วงโซ่สามารถรับอินพุตจากอาร์กิวเมนต์เดียวกันกับที่คุณส่งผ่านไปcat(หรือจากอินพุตมาตรฐานหากคุณไม่ส่งอาร์กิวเมนต์) ก็catจะไม่มีประโยชน์อย่างยิ่งและส่งผลให้มีการแยกกระบวนการเพิ่มเติมและมีท่อเพิ่มเติมเท่านั้น สร้างขึ้น
Frédéric Hamidi

11
@ FrédéricHamidiเมื่อแมวไม่มีข้อโต้แย้งหรือข้อโต้แย้งคือ-มันเป็นท่อประจำตัว เมื่อมีอาร์กิวเมนต์ชื่อไฟล์ที่ไม่ใช่เส้นประมากกว่าหนึ่งอาร์กิวเมนต์มันจะกลายเป็นอะไรที่มากกว่าท่อข้อมูลประจำตัวและเริ่มตอบสนองวัตถุประสงค์ที่แท้จริง
kojiro

3
ลิงก์ที่เป็นที่นิยมเดิมไปยัง partmaps.org น่าเสียดายที่ตายไปแล้ว ตอนนี้เนื้อหาอยู่ที่porkmail.org/era/unix/award.html
tripleee

1
ดูเพิ่มเติม: unix.stackexchange.com/q/511827/20336
Mikko Rantalainen

2
ฉันสังเกตว่าถ้าคุณต้องการแสดงกระแสข้อมูลที่ถูกต้อง (เหตุผลที่ 1) คุณสามารถทำได้โดยวางการเปลี่ยนเส้นทางไฟล์ไว้หน้าคำสั่งเช่นเดียวกับ<file command1 | command2แม้ว่าจะมีความไม่เห็นด้วยเกี่ยวกับสุนทรียศาสตร์ก็ตาม
holdenweb

คำตอบ:


82

ฉันไม่ทราบถึงรางวัลจนถึงวันนี้เมื่อมือใหม่บางคนพยายามตรึงUUOCไว้กับฉันเพื่อหาคำตอบของฉัน มันเป็นcat file.txt | grep foo | cut ... | cut .... ฉันให้ความคิดของฉันกับเขาและหลังจากนั้นก็ไปที่ลิงค์ที่เขาให้ฉันอ้างถึงที่มาของรางวัลและแนวทางปฏิบัติในการทำเช่นนั้น การค้นหาเพิ่มเติมทำให้ฉันไปสู่คำถามนี้ น่าเสียดายที่แม้จะมีการพิจารณาอย่างมีสติ แต่ก็ไม่มีคำตอบใดรวมถึงเหตุผลของฉัน

ฉันไม่ได้ตั้งใจที่จะตั้งรับในการตอบโต้เขา ท้ายที่สุดในช่วงอายุน้อย ๆ ของฉันฉันจะเขียนคำสั่งgrep foo file.txt | cut ... | cut ...เพราะเมื่อใดก็ตามที่คุณทำซิงเกิ้ลบ่อยๆgrepคุณจะได้เรียนรู้ตำแหน่งของอาร์กิวเมนต์ไฟล์และมันก็พร้อมที่จะรู้ว่าอันแรกคือรูปแบบและหลังจากนั้นคือชื่อไฟล์

มันเป็นทางเลือกที่ใส่ใจที่จะใช้catเมื่อฉันตอบคำถามส่วนหนึ่งเป็นเพราะเหตุผลของ "รสนิยมที่ดี" (ในคำพูดของ Linus Torvalds) แต่ส่วนใหญ่เป็นเหตุผลที่น่าสนใจในการทำงาน

เหตุผลประการหลังสำคัญกว่าดังนั้นฉันจะเอาออกก่อน เมื่อฉันเสนอไปป์ไลน์เป็นโซลูชันฉันคาดว่าจะสามารถใช้ซ้ำได้ มีความเป็นไปได้ค่อนข้างมากที่จะมีการเพิ่มไปป์ไลน์ที่ส่วนท้ายหรือต่อเข้าไปในท่ออื่น ในกรณีที่มีการโต้แย้งไฟล์ไปยังสกรู grep ขึ้นสามารถนำมาใช้และค่อนข้างเป็นไปได้ทำเช่นนั้นอย่างเงียบ ๆโดยไม่มีข้อความข้อผิดพลาดถ้าอาร์กิวเมนต์ไฟล์ที่มีอยู่ I. จ. grep foo xyz | grep bar xyz | wcจะทำให้คุณมีวิธีการหลายเส้นในxyzมีbarในขณะที่คุณจะคาดหวังว่าจำนวนของสายที่มีทั้งในและfoo barการต้องเปลี่ยนอาร์กิวเมนต์เป็นคำสั่งในไปป์ไลน์ก่อนใช้งานมีแนวโน้มที่จะเกิดข้อผิดพลาด เพิ่มความเป็นไปได้ที่จะเกิดความล้มเหลวโดยไม่ได้ตั้งใจและกลายเป็นการปฏิบัติที่ร้ายกาจโดยเฉพาะ

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

อย่างไรก็ตามฉันจะพยายามหาเหตุผล "รสชาติดี" ในอดีตที่ฉันพูดถึง เหตุผลดังกล่าวเกี่ยวข้องกับจิตวิญญาณการออกแบบที่ตั้งฉากกันของ Unix grepไม่ได้cutและไม่ได้ls grepดังนั้นอย่างน้อยที่สุดก็grep foo file1 file2 file3ขัดกับจิตวิญญาณของการออกแบบ cat file1 file2 file3 | grep fooวิธีทำมุมฉากของมันคือ ตอนนี้grep foo file1เป็นเพียงกรณีพิเศษgrep foo file1 file2 file3และถ้าคุณไม่ปฏิบัติเช่นเดียวกันอย่างน้อยคุณก็ใช้วงจรนาฬิกาสมองพยายามหลีกเลี่ยงรางวัลแมวไร้ประโยชน์

นั่นนำเราไปสู่การโต้เถียงที่grep foo file1 file2 file3เชื่อมต่อกันและcatเชื่อมต่อกันดังนั้นจึงเป็นเรื่องที่เหมาะสมcat file1 file2 file3แต่เนื่องจากcatไม่ได้เชื่อมต่อเข้าด้วยกันcat file1 | grep fooดังนั้นเราจึงละเมิดจิตวิญญาณของcatUnix ทั้งสองและผู้ทรงอำนาจ ถ้าเป็นเช่นนั้น Unix จะต้องใช้คำสั่งอื่นในการอ่านเอาต์พุตของไฟล์หนึ่งไฟล์และถ่มน้ำลายเป็น stdout (ไม่ใช่เลขหน้าหรืออะไรก็ตามที่เป็นเพียงแค่การถ่มน้ำลายเท่านั้น) ดังนั้นคุณจะมีสถานการณ์ที่คุณพูดcat file1 file2หรือพูดdog file1และจำไว้อย่างมีมโนธรรมว่าหลีกเลี่ยงcat file1เพื่อหลีกเลี่ยงการได้รับรางวัลในขณะเดียวกันก็หลีกเลี่ยงdog file1 file2เนื่องจากหวังว่าการออกแบบdogจะทำให้เกิดข้อผิดพลาดหากระบุไฟล์หลายไฟล์

หวังว่า ณ จุดนี้คุณจะเห็นอกเห็นใจนักออกแบบ Unix ที่ไม่รวมคำสั่งแยกต่างหากเพื่อคายไฟล์ไปยัง stdout ในขณะที่ตั้งชื่อcatให้เรียงต่อกันแทนที่จะตั้งชื่ออื่น ในความเป็นจริง<edit>ลบความคิดเห็นที่ไม่ถูกต้องออกเป็นสิ่งอำนวยความสะดวกที่ไม่มีการคัดลอกที่มีประสิทธิภาพในการพ่นไฟล์ไปยัง stdout ซึ่งคุณสามารถวางตำแหน่งที่จุดเริ่มต้นของไปป์ไลน์เพื่อให้นักออกแบบ Unix รวมบางอย่างไว้สำหรับสิ่งนี้โดยเฉพาะ<<</edit>

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

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

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


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

38
ตามที่ระบุไว้ในคำตอบโดยKojiro< file command1 ...เป็นอย่างดีที่สุดและตามกฎหมายที่จะเริ่มต้นกับท่อ แม้ว่าตำแหน่งทั่วไปสำหรับตัวดำเนินการเปลี่ยนเส้นทาง I / O จะอยู่หลังชื่อคำสั่งและอาร์กิวเมนต์นั่นเป็นเพียงแบบแผนเท่านั้นไม่ใช่ตำแหน่งบังคับ <ไม่ต้องนำหน้าชื่อไฟล์ ดังนั้นมีความใกล้เคียงกับความสมมาตรที่สมบูรณ์แบบระหว่าง>outputและเปลี่ยนเส้นทาง:<input <input command1 -opt 1 | command2 -o | command3 >output
Jonathan Leffler

15
ฉันคิดว่าเหตุผลหนึ่งที่ผู้คนโยนหิน UUoC (รวมถึงฉันด้วย) คือการให้ความรู้เป็นหลัก บางครั้งผู้คนประมวลผลไฟล์ข้อความขนาดใหญ่กิกะไบต์ซึ่งในกรณีนี้การย่อขนาดท่อ (UUoC การยุบ greps ตามลำดับให้เป็นหนึ่ง aso) เป็นสิ่งสำคัญและบ่อยครั้งที่สามารถสันนิษฐานได้อย่างปลอดภัยตามคำถามที่ OP ไม่รู้จริงๆว่าการปรับแต่งเล็กน้อยอาจมี ผลกระทบด้านประสิทธิภาพอย่างมาก ฉันเห็นด้วยกับประเด็นของคุณเกี่ยวกับวัฏจักรของสมองและนั่นคือเหตุผลที่ฉันพบว่าตัวเองใช้แมวเป็นประจำแม้ว่าจะไม่จำเป็นก็ตาม แต่สิ่งสำคัญคือต้องรู้ว่ามันไม่จำเป็น
Adrian Frühwirth

13
โปรดเข้าใจ; ฉันไม่รู้สึกว่าcatมันไม่มีประโยชน์ ไม่ใช่ว่าcatไม่มีประโยชน์ catมันคือการที่สร้างโดยเฉพาะอย่างยิ่งไม่จำเป็นต้องใช้ หากคุณต้องการโปรดทราบว่านั่นคือUUoC (Useless Use of cat) ไม่ใช่ UoUC (Use of Useless cat) มีหลายครั้งที่catจะใช้เครื่องมือที่ถูกต้อง ฉันไม่มีปัญหากับการใช้งานเมื่อเป็นเครื่องมือที่ถูกต้องที่จะใช้ (และพูดถึงกรณีในคำตอบของฉัน)
Jonathan Leffler

6
@randomstring ฉันได้ยินคุณ แต่ฉันคิดว่ามันขึ้นอยู่กับกรณีการใช้งานจริงๆ เมื่อใช้ในบรรทัดคำสั่งเพิ่มเติมหนึ่งรายการcatในไปป์อาจไม่ใช่เรื่องใหญ่ขึ้นอยู่กับข้อมูล แต่เมื่อใช้เป็นสภาพแวดล้อมการเขียนโปรแกรมอาจจำเป็นอย่างยิ่งที่จะต้องใช้สิ่งที่สำคัญต่อประสิทธิภาพเหล่านี้ โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับbashสิ่งที่ฉลาดด้านประสิทธิภาพเปรียบเสมือนล้อทรงสี่เหลี่ยม (เมื่อเทียบกับkshอย่างไรก็ตามฉันกำลังพูดถึงที่นี่ช้าลงถึง 10 เท่า - ไม่ล้อเล่น) คุณไม่ต้องการเพิ่มประสิทธิภาพส้อมของคุณ (และไม่เพียงแค่นั้น) เมื่อจัดการกับสคริปต์ขนาดใหญ่หรือลูปขนาดใหญ่
Adrian Frühwirth

58

ไม่!

ก่อนอื่นไม่สำคัญว่าคำสั่งจะเกิดขึ้นที่ใด ดังนั้นหากคุณชอบการเปลี่ยนเส้นทางไปทางซ้ายของคำสั่งก็ไม่เป็นไร:

< somefile command

เหมือนกับ

command < somefile

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

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

for word in $(cat somefile);  # for word in $(<somefile); … (or better yet, while read < somefile)

grep something | awk stuff; # awk '/something/ stuff' (similar for sed)

echo something | command; # command <<< something (although echo would be necessary for pure POSIX)

อย่าลังเลที่จะแก้ไขเพื่อเพิ่มตัวอย่างเพิ่มเติม


2
การเพิ่มความเร็วคงไม่มาก
Dakkaron

9
การวาง "<somefile" ก่อน "คำสั่ง" ในทางเทคนิคจะช่วยให้คุณอ่านจากซ้ายไปขวา แต่จะทำให้การอ่านไม่ชัดเจนเนื่องจากไม่มีการแบ่งเขตทางวากยสัมพันธ์: < cat grep dogเป็นตัวอย่างที่สร้างขึ้นเพื่อแสดงว่าคุณไม่สามารถบอกได้อย่างง่ายดายระหว่างไฟล์อินพุตคำสั่ง ที่รับอินพุตและอาร์กิวเมนต์ของคำสั่ง
necromancer

2
กฎของหัวแม่มือผมเคยนำมาใช้สำหรับการตัดสินใจที่เปลี่ยนเส้นทาง STDIN ไปคือการทำสิ่งที่ช่วยลดการปรากฏตัวของความคลุมเครือ / ที่มีศักยภาพสำหรับความประหลาดใจ พูดแบบดันทุรังบอกว่ามันจะไปก่อนที่จะทำให้เกิดปัญหาของเนโครแมนเซอร์ขึ้นมา แต่ในทางดันทุรังบอกว่าหลังจากนั้นก็สามารถทำสิ่งเดียวกันได้ พิจารณา: stdout=$(foo bar -exec baz <qux | ENV=VAR quux). ถาม<quxใช้กับfooหรือกับbazซึ่งเป็น-exec'd by foo? A. ใช้ได้กับfooแต่อาจดูคลุมเครือ ใส่<qux ก่อน ในกรณีนี้มีความชัดเจนมากขึ้นแม้จะน้อยกว่าปกติและจะคล้ายคลึงกับการต่อท้ายfoo ENV=VAR quux
Mark G.

3
@necromancer <"cat" grep dogอ่านง่ายกว่ามี (โดยปกติฉันเป็นช่องว่างแบบโปร แต่กรณีนี้เป็นข้อยกเว้นอย่างมาก)
Charles Duffy

1
@kojiro "มันช้าที่สุดแล้ว" คุณไม่สามารถเขียนได้โดยไม่ต้องสำรองข้อมูลด้วยตัวเลข หมายเลขของฉันอยู่ที่นี่: oletange.blogspot.com/2013/10/useless-use-of-cat.html (และแสดงว่าช้าลงเมื่อคุณมีรางน้ำสูงเท่านั้น) ของคุณอยู่ที่ไหน
Ole Tange

30

ฉันไม่เห็นด้วยกับอินสแตนซ์ส่วนใหญ่ของรางวัล UUOC Award ที่ใจร้อนเกินไปเพราะเมื่อสอนคนอื่นcatจะเป็นตัวยึดที่สะดวกสำหรับคำสั่งใด ๆ หรือไปป์ไลน์คำสั่งที่ซับซ้อนซึ่งให้ผลลัพธ์ที่เหมาะสมกับปัญหาหรืองานที่กำลังกล่าวถึง

โดยเฉพาะอย่างยิ่งในไซต์ต่างๆเช่น Stack Overflow, ServerFault, Unix & Linux หรือไซต์ SE ใด ๆ

หากมีคนถามเป็นพิเศษเกี่ยวกับการเพิ่มประสิทธิภาพหรือถ้าคุณรู้สึกว่าต้องการเพิ่มข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ขอให้พูดคุยเกี่ยวกับการใช้ cat ที่ไม่มีประสิทธิภาพ แต่อย่าดูถูกผู้คนเพราะพวกเขาเลือกที่จะมุ่งเป้าไปที่ความเรียบง่ายและง่ายต่อการเข้าใจในตัวอย่างของพวกเขามากกว่าที่จะมองมาที่ฉันว่าฉันเจ๋งแค่ไหน! ความซับซ้อน

ในระยะสั้นเพราะแมวไม่ใช่แมวเสมอไป

เพราะคนส่วนใหญ่ที่ชอบให้รางวัล UUOCs ทำเช่นนั้นเพราะพวกเขากังวลกับการอวดว่าพวกเขา 'ฉลาด' แค่ไหนในการช่วยเหลือหรือสอนผู้คน ในความเป็นจริงพวกเขาแสดงให้เห็นว่าพวกเขาอาจเป็นเพียงมือใหม่อีกคนที่ได้พบไม้เล็ก ๆ ที่จะเอาชนะเพื่อนร่วมงานได้


อัปเดต

นี่คือ UUOC อื่นที่ฉันโพสต์ในคำตอบที่https://unix.stackexchange.com/a/301194/7696 :

sqlq() {
  local filter
  filter='cat'

  # very primitive, use getopts for real option handling.
  if [ "$1" == "--delete-blank-lines" ] ; then
    filter='grep -v "^$"'
    shift
  fi

  # each arg is piped into sqlplus as a separate command
  printf "%s\n" "$@" | sqlplus -S sss/eee@sid | $filter
}

ผู้ใช้ UUOC จะบอกว่านั่นคือ UUOC เพราะเป็นไปได้อย่างง่ายดายที่จะ$filterตั้งค่าเริ่มต้นให้กับสตริงว่างและมีifคำสั่งทำfilter='| grep -v "^$"'แต่ IMO โดยการไม่ฝังอักขระไปป์ใน$filterนั้น "ไร้ประโยชน์" catนี้ทำหน้าที่เป็นประโยชน์อย่างยิ่งในการจัดทำเอกสารข้อเท็จจริงด้วยตนเอง ที่$filterอยู่บนprintfเส้นไม่ได้เป็นเพียงการโต้เถียงอีกครั้งเพื่อให้sqlplusมันเป็นผู้ใช้สามารถเลือกตัวเลือกตัวกรองเอาท์พุท

หากมีความจำเป็นใด ๆ ที่จะมีหลายตัวเลือกฟิลเตอร์ส่งออก, การประมวลผลตัวเลือกที่สามารถผนวกเพียง| whateverเพื่อ$filterบ่อยเท่าที่ต้องการ - หนึ่งเป็นพิเศษcatในท่อจะไม่เจ็บอะไรหรือก่อให้เกิดการสูญเสียที่เห็นได้ชัดเจนของการปฏิบัติงานใด ๆ


12
เช่นกัน - ==ภายใน[ ]ไม่ได้ระบุโดย POSIX และไม่ใช้งานทั้งหมดยอมรับมัน =ผู้ประกอบการที่ได้มาตรฐานเป็นเพียง
Charles Duffy

27

ด้วยเวอร์ชัน UUoC catต้องอ่านไฟล์ลงในหน่วยความจำจากนั้นจึงเขียนออกไปยังไพพ์และคำสั่งต้องอ่านข้อมูลจากไพพ์ดังนั้นเคอร์เนลจึงต้องคัดลอกไฟล์ทั้งหมดสามครั้งในกรณีที่เปลี่ยนทิศทาง เคอร์เนลต้องคัดลอกไฟล์เพียงครั้งเดียว การทำบางสิ่งหนึ่งครั้งทำได้เร็วกว่าการทำสามครั้ง

ใช้:

cat "$@" | command

catคือการใช้เครือจำเป็นต้องไร้ประโยชน์ที่แตกต่างกันและไม่ได้มาจาก จะยังไม่มีประโยชน์หากคำสั่งเป็นตัวกรองมาตรฐานที่ยอมรับอาร์กิวเมนต์ชื่อไฟล์เป็นศูนย์หรือมากกว่าและประมวลผลในทางกลับกัน พิจารณาtrคำสั่ง: เป็นตัวกรองบริสุทธิ์ที่ละเว้นหรือปฏิเสธอาร์กิวเมนต์ชื่อไฟล์ ในการป้อนไฟล์หลายไฟล์คุณต้องใช้catดังที่แสดง (แน่นอนมีการอภิปรายแยกกันว่าการออกแบบtrไม่ดีมากไม่มีเหตุผลที่แท้จริงที่ไม่สามารถออกแบบเป็นตัวกรองมาตรฐานได้) สิ่งนี้อาจใช้ได้เช่นกันหากคุณต้องการให้คำสั่งถือว่าอินพุตทั้งหมดเป็น ไฟล์เดียวแทนที่จะเป็นไฟล์แยกกันหลายไฟล์แม้ว่าคำสั่งจะยอมรับไฟล์แยกกันหลายไฟล์ก็ตามตัวอย่างwcเช่นเป็นคำสั่ง

เป็นcat single-fileกรณีที่ไม่มีเงื่อนไขโดยไม่มีเงื่อนไข


27

ในการป้องกันแมว:

ใช่,

   < input process > output 

หรือ

   process < input > output 

มีประสิทธิภาพมากกว่า แต่การเรียกจำนวนมากไม่มีปัญหาด้านประสิทธิภาพคุณจึงไม่สนใจ

เหตุผลด้านการยศาสตร์:

เราใช้ในการอ่านจากซ้ายไปขวาดังนั้นคำสั่งเช่น

    cat infile | process1 | process2 > outfile

เป็นเรื่องเล็กน้อยที่จะเข้าใจ

    process1 < infile | process2 > outfile

ต้องข้ามไปที่ process1 แล้วอ่านจากซ้ายไปขวา สิ่งนี้สามารถรักษาได้โดย:

    < infile process1 | process2 > outfile

ดูเหมือนว่ามีลูกศรชี้ไปทางซ้ายโดยที่ไม่มีอะไรอยู่ สับสนมากขึ้นและดูเหมือนการอ้างถึงแฟนซีคือ:

    process1 > outfile < infile

และการสร้างสคริปต์มักจะเป็นกระบวนการซ้ำ ๆ

    cat file 
    cat file | process1
    cat file | process1 | process2 
    cat file | process1 | process2 > outfile

ที่คุณเห็นความคืบหน้าในขณะที่

    < file 

ไม่ได้ผล วิธีง่ายๆคือข้อผิดพลาดน้อยลงและ catenation คำสั่งตามหลักสรีรศาสตร์ทำได้ง่ายสำหรับ cat

อีกหัวข้อหนึ่งคือคนส่วนใหญ่ได้สัมผัสกับ> และ <ในฐานะตัวดำเนินการเปรียบเทียบก่อนที่จะใช้คอมพิวเตอร์และเมื่อใช้คอมพิวเตอร์เป็นโปรแกรมเมอร์มักจะสัมผัสกับสิ่งเหล่านี้มากขึ้น

และการเปรียบเทียบตัวถูกดำเนินการสองตัวกับ <และ> นั้นตรงกันข้ามกับการสับเปลี่ยนซึ่งหมายความว่า

(a > b) == (b < a)

ฉันจำได้ว่าครั้งแรกที่ใช้ <สำหรับการเปลี่ยนทิศทางอินพุตฉันกลัว

a.sh < file 

อาจมีความหมายเหมือนกับ

file > a.sh

และเขียนทับสคริปต์ a.sh ของฉันด้วยวิธีใด อาจเป็นปัญหาสำหรับผู้เริ่มต้นหลายคน

ความแตกต่างที่หายาก

wc -c journal.txt
15666 journal.txt
cat journal.txt | wc -c 
15666

หลังสามารถใช้ในการคำนวณได้โดยตรง

factor $(cat journal.txt | wc -c)

แน่นอนว่า <สามารถใช้ที่นี่ได้เช่นกันแทนที่จะเป็นพารามิเตอร์ไฟล์:

< journal.txt wc -c 
15666
wc -c < journal.txt
15666
    

แต่ใครจะสน - 15k?

ถ้าฉันเจอปัญหาเป็นครั้งคราวฉันจะเปลี่ยนนิสัยชอบเรียกแมว

เมื่อใช้ไฟล์ขนาดใหญ่มากหรือหลายไฟล์หลาย ๆ ไฟล์การหลีกเลี่ยง cat ก็ทำได้ดี สำหรับคำถามส่วนใหญ่การใช้แมวเป็นเรื่องที่ตั้งฉากกันนอกหัวข้อไม่ใช่ปัญหา

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


6
+11111 .. ในฐานะผู้เขียนคำตอบที่ได้รับการยอมรับในปัจจุบันฉันขอแนะนำส่วนเสริมที่น่ายินดีนี้ ตัวอย่างเฉพาะอธิบายข้อโต้แย้งที่เป็นนามธรรมและเป็นคำพูดของฉันบ่อยครั้งและเสียงหัวเราะที่คุณได้รับจากความกังวลใจในช่วงต้นของผู้เขียนfile > a.shนั้นคุ้มค่ากับเวลาที่อ่านสิ่งนี้ :) ขอบคุณสำหรับการแบ่งปัน!
necromancer

ในการภาวนานี้cat file | wc -c, wcความต้องการที่จะอ่าน stdin จนกว่า EOF นับไบต์ แต่ในนี้wc -c < fileมันเป็นเพียงสถิติ stdin พบว่ามันเป็นไฟล์ปกติและพิมพ์ st_size แทนการอ่านอินพุตใด ๆ สำหรับไฟล์ขนาดใหญ่ประสิทธิภาพจะแตกต่างกันอย่างชัดเจน
oguz ismail

18

ปัญหาเพิ่มเติมคือไปป์สามารถปกปิด subshell ได้อย่างเงียบ ๆ สำหรับตัวอย่างนี้ฉันจะแทนที่catด้วยechoแต่มีปัญหาเดียวกัน

echo "foo" | while read line; do
    x=$line
done

echo "$x"

คุณอาจคาดหวังว่าxจะมีfooแต่ไม่มี xคุณตั้งอยู่ใน subshell กลับกลายเพื่อรันwhileห่วง xในเชลล์ที่เริ่มต้นไปป์ไลน์มีค่าที่ไม่สัมพันธ์กันหรือไม่ได้ตั้งค่าเลย

ใน bash4 คุณสามารถกำหนดค่าตัวเลือกเชลล์บางตัวเพื่อให้คำสั่งสุดท้ายของไปป์ไลน์ดำเนินการในเชลล์เดียวกันกับที่เริ่มต้นไปป์ไลน์ แต่คุณอาจลองทำเช่นนี้

echo "foo" | while read line; do
    x=$line
done | awk '...'

และxเป็นอีกครั้งในพื้นที่whileของ subshell


5
ในเชลล์ POSIX อย่างเคร่งครัดสิ่งนี้อาจเป็นปัญหาที่ยุ่งยากเนื่องจากคุณไม่มีสตริงหรือการแทนที่กระบวนการที่นี่เพื่อหลีกเลี่ยงไปป์ BashFAQ 24มีวิธีแก้ปัญหาที่เป็นประโยชน์แม้ในกรณีนั้น
kojiro

4
ในบางเชลล์ไปป์ในภาพประกอบจะไม่สร้างเชลล์ย่อย ตัวอย่างเช่น Korn และ Z นอกจากนี้ยังสนับสนุนการทดแทนกระบวนการและสตริงที่นี่ แน่นอนว่าพวกเขาไม่ได้เป็นPOSIX อย่างเคร่งครัด Bash 4 shopt -s lastpipeต้องหลีกเลี่ยงการสร้าง subshell
หยุดชั่วคราวจนกว่าจะมีประกาศอีกครั้ง

14

ในฐานะที่เป็นคนที่ชี้ให้เห็นสิ่งนี้เป็นประจำและโปรแกรมต่อต้านรูปแบบการเขียนโปรแกรมเชลล์อื่น ๆ อีกจำนวนมากฉันรู้สึกว่าจำเป็นต้องชั่งน้ำหนักอย่างล่าช้า

เชลล์สคริปต์เป็นภาษาคัดลอก / วางเป็นอย่างมาก สำหรับคนส่วนใหญ่ที่เขียนเชลล์สคริปต์พวกเขาไม่ได้อยู่ในนั้นเพื่อเรียนรู้ภาษา มันเป็นเพียงอุปสรรคที่พวกเขาต้องเอาชนะเพื่อที่จะทำสิ่งต่างๆในภาษาที่พวกเขาคุ้นเคยอยู่บ้าง

ในบริบทนั้นฉันเห็นว่ามันก่อกวนและอาจเป็นอันตรายได้แม้กระทั่งการเผยแพร่รูปแบบการต่อต้านการเขียนสคริปต์เชลล์ต่างๆ รหัสที่ใครบางคนพบใน Stack Overflow ควรเป็นไปได้ที่จะคัดลอก / วางลงในสภาพแวดล้อมของพวกเขาโดยมีการเปลี่ยนแปลงเพียงเล็กน้อยและความเข้าใจที่ไม่สมบูรณ์

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

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

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

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


2
นอกจากนี้สำหรับไฟล์ขนาดใหญ่การไพพ์ผ่านcatเป็นสวิตช์บริบทเพิ่มเติมและแบนด์วิดท์หน่วยความจำจำนวนมาก (และมลพิษของแคช L3 จากสำเนาข้อมูลเพิ่มเติมในcatบัฟเฟอร์การอ่านและบัฟเฟอร์ไปป์) โดยเฉพาะอย่างยิ่งในเครื่องมัลติคอร์ขนาดใหญ่ (เช่นการตั้งค่าโฮสติ้งจำนวนมาก) แบนด์วิดท์แคช / หน่วยความจำเป็นทรัพยากรที่ใช้ร่วมกัน
Peter Cordes

1
@PeterCordes กรุณาโพสต์การวัดของคุณ ดังนั้นเราสามารถทำได้ถ้ามันมีความสำคัญในทางปฏิบัติ ประสบการณ์ของฉันคือปกติมันไม่สำคัญ: oletange.blogspot.com/2013/10/useless-use-of-cat.html
Ole Tange

1
บล็อกของคุณเองแสดงการชะลอตัว 50% สำหรับปริมาณงานสูงและคุณไม่ได้มองถึงผลกระทบต่อปริมาณงานทั้งหมด (หากคุณมีสิ่งที่ทำให้คอร์อื่น ๆ ไม่ว่าง) ถ้าฉันเข้าใจฉันอาจทำการทดสอบของคุณในขณะที่ x264 หรือ x265 กำลังเข้ารหัสวิดีโอโดยใช้คอร์ทั้งหมดและดูว่าการเข้ารหัสวิดีโอช้าลงมากแค่ไหน bzip2และการgzipบีบอัดนั้นช้ามากเมื่อเทียบกับจำนวนค่าโสหุ้ยที่catเพิ่มเข้าไปเพียงอย่างเดียว (โดยที่เครื่องไม่ได้ใช้งาน) อ่านตารางของคุณได้ยาก (ขีดเส้นไว้ตรงกลางตัวเลข?) sysเวลาเพิ่มขึ้นมาก แต่ยังน้อยเทียบกับผู้ใช้หรือของจริง?
Peter Cordes

8

ฉันมักจะใช้cat file | myprogramในตัวอย่าง บางครั้งฉันถูกกล่าวหาว่าใช้แมวอย่างไร้ประโยชน์ ( http://porkmail.org/era/unix/award.html ) ฉันไม่เห็นด้วยด้วยเหตุผลต่อไปนี้:

  • เป็นเรื่องง่ายที่จะเข้าใจสิ่งที่เกิดขึ้น

    เมื่ออ่านคำสั่ง UNIX คุณคาดว่าจะมีคำสั่งตามด้วยอาร์กิวเมนต์ตามด้วยการเปลี่ยนเส้นทาง เป็นไปได้ที่จะนำการเปลี่ยนเส้นทางไปที่ใดก็ได้ แต่ไม่ค่อยมีให้เห็น - ดังนั้นผู้คนจะมีเวลาอ่านตัวอย่างยากขึ้น ฉันเชื่อ

    cat foo | program1 -o option -b option | program2

    อ่านง่ายกว่า

    program1 -o option -b option < foo | program2

    หากคุณย้ายการเปลี่ยนเส้นทางไปที่จุดเริ่มต้นคุณจะสร้างความสับสนให้กับผู้ที่ไม่คุ้นเคยกับไวยากรณ์นี้:

    < foo program1 -o option -b option | program2

    และตัวอย่างควรเข้าใจง่าย

  • มันเป็นเรื่องง่ายที่จะเปลี่ยน

    หากคุณรู้ว่าโปรแกรมสามารถอ่านได้catโดยปกติคุณสามารถสันนิษฐานได้ว่าสามารถอ่านเอาต์พุตจากโปรแกรมใด ๆ ที่ส่งออกไปยัง STDOUT ได้ดังนั้นคุณจึงสามารถปรับให้เข้ากับความต้องการของคุณเองและได้ผลลัพธ์ที่คาดเดาได้

  • เป็นการเน้นว่าโปรแกรมไม่ล้มเหลวหาก STDIN ไม่ใช่ไฟล์

    ไม่ปลอดภัยที่จะคิดว่าถ้าได้program1 < fooผลแล้วก็cat foo | program1จะได้ผลเช่นกัน อย่างไรก็ตามมันปลอดภัยที่จะคิดตรงกันข้าม โปรแกรมนี้ใช้งานได้หาก STDIN เป็นไฟล์ แต่จะล้มเหลวหากอินพุตเป็นไพพ์เนื่องจากใช้การค้นหา:

    # works
    < foo perl -e 'seek(STDIN,1,1) || die;print <STDIN>'
    
    # fails
    cat foo | perl -e 'seek(STDIN,1,1) || die;print <STDIN>'

ต้นทุนประสิทธิภาพ

catมีค่าใช้จ่ายในการดำเนินการเพิ่มเติมคือ เพื่อให้ทราบว่าฉันทำการทดสอบสองสามครั้งเพื่อจำลองพื้นฐาน ( cat), ทรูพุตต่ำ ( bzip2), ปริมาณงานปานกลาง ( gzip) และปริมาณงานสูง ( grep)

cat $ISO | cat
< $ISO cat
cat $ISO | bzip2
< $ISO | bzip2
cat $ISO | gzip
< $ISO gzip
cat $ISO | grep no_such_string
< $ISO grep no_such_string

การทดสอบดำเนินการบนระบบต่ำสุด (0.6 GHz) และแล็ปท็อปธรรมดา (2.2 GHz) มีการเรียกใช้ 10 ครั้งในแต่ละระบบและเลือกเวลาที่ดีที่สุดเพื่อเลียนแบบสถานการณ์ที่เหมาะสมที่สุดสำหรับการทดสอบแต่ละครั้ง ISO $ คือ ubuntu-11.04-desktop-i386.iso (ตาราง Prettier ที่นี่: http://oletange.blogspot.com/2013/10/useless-use-of-cat.html )

CPU                       0.6 GHz ARM
Command                   cat $ISO|                        <$ISO                            Diff                             Diff (pct)
Throughput \ Time (ms)    User       Sys        Real       User       Sys        Real       User       Sys        Real       User       Sys        Real
Baseline (cat)                     55      14453      33090         23       6937      33126         32       7516        -36        239        208         99
Low (bzip2)                   1945148      16094    1973754    1941727       5664    1959982       3420      10430      13772        100        284        100
Medium (gzip)                  413914      13383     431812     407016       5477     416760       6898       7906      15052        101        244        103
High (grep no_such_string)      80656      15133      99049      79180       4336      86885       1476      10797      12164        101        349        114

CPU                       Core i7 2.2 GHz
Command                   cat $ISO|           <$ISO             Diff          Diff (pct)
Throughput \ Time (ms)    User     Sys Real   User   Sys Real   User Sys Real User       Sys Real
Baseline (cat)                    0 356    215      1  84     88    0 272  127          0 423  244
Low (bzip2)                  136184 896 136765 136728 160 137131 -545 736 -366         99 560   99
Medium (gzip)                 26564 788  26791  26332 108  26492  232 680  298        100 729  101
High (grep no_such_string)      264 392    483    216  84    304   48 308  179        122 466  158

ผลลัพธ์แสดงให้เห็นว่าสำหรับปริมาณงานต่ำและปานกลางต้นทุนจะอยู่ในลำดับ 1% สิ่งนี้อยู่ในความไม่แน่นอนของการวัดดังนั้นในทางปฏิบัติจึงไม่มีความแตกต่าง

สำหรับปริมาณงานสูงความแตกต่างจะใหญ่กว่าและมีความแตกต่างที่ชัดเจนระหว่างทั้งสอง

ที่นำไปสู่ข้อสรุป: คุณควรใช้<แทนcat |if:

  • ความซับซ้อนของการประมวลผลนั้นคล้ายกับ grep ธรรมดา
  • ประสิทธิภาพมีความสำคัญมากกว่าความสามารถในการอ่าน

มิฉะนั้นมันไม่สำคัญว่าคุณจะใช้หรือ<cat |

ดังนั้นคุณควรให้รางวัล UUoC ก็ต่อเมื่อ:

  • คุณสามารถวัดความแตกต่างอย่างมีนัยสำคัญในประสิทธิภาพ (เผยแพร่การวัดของคุณเมื่อคุณให้รางวัล)
  • ประสิทธิภาพมีความสำคัญมากกว่าความสามารถในการอ่าน

-3

ฉันคิดว่า (วิธีดั้งเดิม) โดยใช้ท่อนั้นเร็วกว่าเล็กน้อย ในกล่องของฉันฉันใช้straceคำสั่งเพื่อดูว่าเกิดอะไรขึ้น:

ไม่มีท่อ:

toc@UnixServer:~$ strace wc -l < wrong_output.c
execve("/usr/bin/wc", ["wc", "-l"], [/* 18 vars */]) = 0
brk(0)                                  = 0x8b50000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ad000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=29107, ...}) = 0
mmap2(NULL, 29107, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb77a5000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1552584, ...}) = 0
mmap2(NULL, 1563160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7627000
mmap2(0xb779f000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x178) = 0xb779f000
mmap2(0xb77a2000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb77a2000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7626000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb76268d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb779f000, 8192, PROT_READ)   = 0
mprotect(0x804f000, 4096, PROT_READ)    = 0
mprotect(0xb77ce000, 4096, PROT_READ)   = 0
munmap(0xb77a5000, 29107)               = 0
brk(0)                                  = 0x8b50000
brk(0x8b71000)                          = 0x8b71000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=5540198, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7426000
mmap2(NULL, 1507328, PROT_READ, MAP_PRIVATE, 3, 0x2a8) = 0xb72b6000
close(3)                                = 0
open("/usr/share/locale/locale.alias", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb77ac000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2570
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0xb77ac000, 4096)                = 0
open("/usr/share/locale/fr_FR.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr_FR.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr_FR/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/fr/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr_FR.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr_FR.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr_FR/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/fr/LC_MESSAGES/coreutils.mo", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=316721, ...}) = 0
mmap2(NULL, 316721, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7268000
close(3)                                = 0
open("/usr/lib/i386-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=26064, ...}) = 0
mmap2(NULL, 26064, PROT_READ, MAP_SHARED, 3, 0) = 0xb7261000
close(3)                                = 0
read(0, "#include<stdio.h>\n\nint main(int "..., 16384) = 180
read(0, "", 16384)                      = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7260000
write(1, "13\n", 313
)                     = 3
close(0)                                = 0
close(1)                                = 0
munmap(0xb7260000, 4096)                = 0
close(2)                                = 0
exit_group(0)                           = ?

และท่อ:

toc@UnixServer:~$ strace cat wrong_output.c | wc -l
execve("/bin/cat", ["cat", "wrong_output.c"], [/* 18 vars */]) = 0
brk(0)                                  = 0xa017000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb774b000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=29107, ...}) = 0
mmap2(NULL, 29107, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7743000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0p\222\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1552584, ...}) = 0
mmap2(NULL, 1563160, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb75c5000
mmap2(0xb773d000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x178) = 0xb773d000
mmap2(0xb7740000, 10776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7740000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb75c4000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75c48d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb773d000, 8192, PROT_READ)   = 0
mprotect(0x8051000, 4096, PROT_READ)    = 0
mprotect(0xb776c000, 4096, PROT_READ)   = 0
munmap(0xb7743000, 29107)               = 0
brk(0)                                  = 0xa017000
brk(0xa038000)                          = 0xa038000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=5540198, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb73c4000
mmap2(NULL, 1507328, PROT_READ, MAP_PRIVATE, 3, 0x2a8) = 0xb7254000
close(3)                                = 0
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
open("wrong_output.c", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0664, st_size=180, ...}) = 0
read(3, "#include<stdio.h>\n\nint main(int "..., 32768) = 180
write(1, "#include<stdio.h>\n\nint main(int "..., 180) = 180
read(3, "", 32768)                      = 0
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
13

คุณสามารถทำการทดสอบstraceและtimeสั่งการด้วยคำสั่งที่มากขึ้นและยาวขึ้นเพื่อการเปรียบเทียบที่ดี


9
ฉันไม่เข้าใจว่าคุณหมายถึงอะไร(วิธีดั้งเดิม) โดยใช้ไปป์หรือทำไมคุณคิดว่าสิ่งนี้straceแสดงให้เห็นว่าเร็วกว่า - straceไม่ได้ติดตามการwc -lดำเนินการในกรณีที่สอง มันติดตามคำสั่งแรกของไปป์ไลน์ที่นี่เท่านั้น
kojiro

@kojiro: ฉันหมายถึงวิธีดั้งเดิม = วิธีที่ใช้มากที่สุด (ฉันคิดว่าเราใช้ไปป์มากกว่าทิศทาง) ฉันไม่สามารถยืนยันได้ว่ามันเร็วกว่าหรือไม่ในการติดตามของฉันฉันเห็นระบบอื่น ๆ เรียกร้องให้ใช้ทิศทาง คุณสามารถใช้โปรแกรม ac และวนซ้ำเพื่อดูได้โดยใช้เวลามากขึ้น สนใจสอบถามได้ค่ะ :)
TOC

3
การเปรียบเทียบแอปเปิ้ลกับแอปเปิ้ลจะวางstrace -f sh -c 'wc -l < wrong_output.c'เคียงข้างstrace -f sh -c 'cat wrong_output.c | wc -l'กัน
Charles Duffy

5
นี่คือผลลัพธ์จาก ideone.com ซึ่งปัจจุบันเห็นได้ชัดว่าไม่มีcat: ideone.com/2w1W42#stderr
tripleee

1
@CharlesDuffy: mkfifoสร้างไปป์ที่มีชื่อ ไปป์ที่ไม่ระบุชื่อถูกตั้งค่าด้วยpipe(2)แล้วฟอร์กและให้พาเรนต์และชายด์ปิดปลายท่อต่างกัน แต่ใช่คำตอบนี้เป็นเรื่องไร้สาระโดยสิ้นเชิงและไม่ได้พยายามนับการโทรของระบบหรือใช้strace -Oในการวัดค่าใช้จ่ายหรือ-rเพื่อประทับเวลาแต่ละสายที่สัมพันธ์กับครั้งสุดท้าย ...
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.