เหตุใดเชลล์จึงไม่แก้ไข“ การใช้แมวที่ไร้ประโยชน์” โดยอัตโนมัติ? [ปิด]


28

หลายคนใช้ oneliners และสคริปต์ที่มีรหัสตามบรรทัด

cat "$MYFILE" | command1 | command2 > "$OUTPUT"

ครั้งแรกที่catมักจะเรียกว่า "การใช้ประโยชน์จากแมว" เพราะในทางเทคนิคมันต้องเริ่มต้นกระบวนการใหม่ (มัก/usr/bin/cat) ซึ่งสามารถหลีกเลี่ยงได้ถ้าคำสั่งได้รับ

< "$MYFILE" command1 | command2 > "$OUTPUT"

เพราะเชลล์จะต้องเริ่มcommand1และชี้stdinไปที่ไฟล์ที่กำหนดเท่านั้น

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


22
คำสั่งเหล่านั้นไม่เท่ากันจริง ๆ เนื่องจากในกรณีหนึ่ง stdin เป็นไฟล์และอีกอันเป็นไพพ์ดังนั้นมันจะไม่เป็นการแปลงที่ปลอดภัยอย่างเคร่งครัด คุณสามารถสร้างระบบที่ทำได้
Michael Homer

14
การที่คุณนึกภาพกรณีใช้ไม่ได้หมายความว่าแอปพลิเคชันไม่ได้รับอนุญาตให้พึ่งพาพฤติกรรมที่ระบุอย่างไร้ประโยชน์ การได้รับข้อผิดพลาดจากlseekยังคงเป็นพฤติกรรมที่กำหนดไว้และอาจทำให้เกิดผลลัพธ์ที่แตกต่างกันพฤติกรรมการปิดกั้นที่แตกต่างกันอาจมีความหมายในเชิงความหมาย ฯลฯ มันจะอนุญาตให้ทำการเปลี่ยนแปลงได้หากคุณรู้ว่าคำสั่งอื่น ๆหรือถ้าคุณไม่สนใจความเข้ากันได้ในระดับนั้น แต่ข้อดีก็คือค่อนข้างเล็ก ฉันคิดว่าการขาดผลประโยชน์ขับเคลื่อนสถานการณ์มากกว่าค่าความสอดคล้อง
Michael Homer

3
เชลล์ได้รับอนุญาตให้ใช้งานcatตัวเองอย่างแน่นอนหรือยูทิลิตี้อื่น ๆ นอกจากนี้ยังได้รับอนุญาตให้รู้ว่ายูทิลิตี้อื่น ๆ ที่เป็นของระบบทำงานอย่างไร (เช่นสามารถรู้ได้ว่าการgrepใช้งานภายนอกที่มาพร้อมกับระบบทำงานอย่างไร) นี่เป็นสิ่งที่ปฏิบัติได้อย่างสมบูรณ์ดังนั้นจึงยุติธรรมเลยที่จะสงสัยว่าทำไมพวกเขาถึงไม่ทำ
Michael Homer

6
@MichaelHomer เช่นมันสามารถทราบวิธีการดำเนินการ grep ภายนอกที่มาพร้อมกับพฤติกรรมระบบgrepดังนั้นเปลือกในขณะนี้มีการพึ่งพาการทำงานของที่ และsed. และawk. และdu. และมีสาธารณูปโภคอื่น ๆ อีกหลายร้อยหากไม่นับพัน
Andrew Henle

19
มันจะไม่ได้สวยสำหรับเชลล์ของฉันที่จะแก้ไขคำสั่งของฉัน
Azor Ahai

คำตอบ:


25

คำสั่ง 2 ไม่เทียบเท่า: พิจารณาการจัดการข้อผิดพลาด:

cat <file that doesn't exist> | less จะสร้างกระแสที่ว่างเปล่าที่จะถูกส่งผ่านไปยังโปรแกรม piped ... เช่นนี้คุณจะจบลงด้วยการแสดงผลที่แสดงอะไร

< <file that doesn't exist> less จะล้มเหลวในการเปิดบาร์แล้วไม่เปิดน้อยเลย

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


1
ฉันจะทำเครื่องหมายคำตอบของคุณว่ายอมรับเพราะฉันคิดว่านี่เป็นความแตกต่างที่สำคัญที่สุดระหว่างไวยากรณ์ทั้งสอง ตัวแปรที่มีcatจะดำเนินการคำสั่งที่สองในไปป์ไลน์ในขณะที่ตัวแปรที่มีเพียงการเปลี่ยนเส้นทางอินพุตจะไม่ดำเนินการคำสั่งเลยหากไฟล์อินพุตขาดหายไป
Mikko Rantalainen

อย่างไรก็ตามโปรดทราบว่า<"missing-file" grep foo | echo 2จะไม่ดำเนินการแต่จะดำเนินการgrep echo
Mikko Rantalainen

51

"การใช้งานที่ไม่มีประโยชน์cat" เป็นเรื่องเกี่ยวกับวิธีเขียนโค้ดของคุณมากกว่าเกี่ยวกับสิ่งที่เรียกใช้จริงเมื่อคุณเรียกใช้งานสคริปต์ มันเป็นรูปแบบการต่อต้านการออกแบบวิธีการเกี่ยวกับบางสิ่งบางอย่างที่อาจจะทำได้ในลักษณะที่มีประสิทธิภาพมากขึ้น มันเป็นความล้มเหลวในการทำความเข้าใจกับวิธีการรวมเครื่องมือที่กำหนดให้ดีที่สุดเพื่อสร้างเครื่องมือใหม่ ฉันขอยืนยันว่าการร้อยหลายsedและ / หรือawkคำสั่งเข้าด้วยกันในไปป์ไลน์บางครั้งอาจกล่าวได้ว่าเป็นอาการของรูปแบบการต่อต้านเดียวกันนี้

การแก้ไขอินสแตนซ์ของ "การใช้งานที่ไร้ประโยชน์cat" ในสคริปต์เป็นสิ่งสำคัญที่สุดในการแก้ไขซอร์สโค้ดของสคริปต์ด้วยตนเอง เครื่องมือเช่นShellCheckสามารถช่วยได้โดยชี้กรณีที่ชัดเจน:

$ cat script.sh
#!/bin/sh
cat file | cat
$ shellcheck script.sh

In script.sh line 2:
cat file | cat
    ^-- SC2002: Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead.

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

เชลล์ไม่จำเป็นต้องรู้ว่าcatมันคืออะไร มันอาจจะเป็นใด ๆคำสั่งจากทุกที่ในของคุณ$PATHหรือฟังก์ชั่น

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

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

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


มันเข้ากันได้อย่างสมบูรณ์แบบสำหรับเชลล์ที่จะรู้ว่าอะไรcatคืออะไรและคำสั่งอื่น ๆ ในไปป์ไลน์ (กฎ as-if) และทำตามนั้นพวกเขาไม่ได้มาที่นี่เพราะมันไม่มีจุดหมายและยากเกินไป
Michael Homer

4
@MichaelHomer ใช่ แต่มันก็ได้รับอนุญาตให้โอเวอร์โหลดคำสั่งมาตรฐานด้วยฟังก์ชั่นที่มีชื่อเดียวกัน
Kusalananda

2
@PhilipCouling มันเป็นไปตามอย่างแน่นอนตราบใดที่มันรู้ว่าไม่มีคำสั่งไปป์ไลน์ดูแล เชลล์ได้รับอนุญาตให้แทนที่ยูทิลิตี้ด้วยบิวด์อินหรือฟังก์ชั่นเชลล์โดยเฉพาะและไม่มีการ จำกัด สภาพแวดล้อมการดำเนินการดังนั้นตราบใดที่ผลลัพธ์ภายนอกนั้นไม่สามารถแยกออกได้ สำหรับกรณีของคุณcat /dev/ttyเป็นคนที่น่าสนใจที่จะ<แตกต่างกันด้วย
Michael Homer

1
@MichaelHomer ดังนั้นตราบใดที่ผลภายนอกจะแยกไม่ออกว่ามันได้รับอนุญาตที่หมายถึงพฤติกรรมของทั้งชุดของสาธารณูปโภคที่ดีที่สุดในลักษณะดังกล่าวไม่สามารถเปลี่ยนแปลง นั่นจะต้องเป็นนรกพึ่งพาสูงสุด
Andrew Henle

3
@MichaelHomer ในฐานะที่เป็นความคิดเห็นอื่น ๆ กล่าวว่าแน่นอนมัน comformant ที่ดีเลิศสำหรับเปลือกที่จะรู้ว่าได้รับการป้อนข้อมูลของ OP มันเป็นไปไม่ได้ที่จะบอกสิ่งที่เป็นcatคำสั่งที่ไม่จริงโดยไม่ต้องรันมัน สำหรับทุกสิ่งที่คุณ (และเชลล์) รู้ว่า OP มีคำสั่งcatในเส้นทางของเธอซึ่งเป็นการจำลองแมวแบบโต้ตอบ "myfile" เป็นเพียงสถานะเกมที่เก็บไว้command1และcommand2กำลังประมวลผลสถิติบางอย่างเกี่ยวกับเซสชันการเล่นปัจจุบัน ...
alephzero

34

เพราะมันไม่ได้ไร้ประโยชน์

ในกรณีของcat file | cmdfd 0(stdin) ของcmdจะเป็นไปป์และในกรณีของcmd <fileมันอาจเป็นไฟล์อุปกรณ์และอื่น ๆ

ไปป์มีซีแมนทิกส์ต่างกันจากไฟล์ปกติและซีแมนทิกส์นั้นไม่ใช่ชุดย่อยของไฟล์ปกติ:

  • ไฟล์ปกติไม่สามารถselect(2)ed หรือpoll(2)ed ในวิธีที่มีความหมาย; select(2)บนมันก็จะกลับมา "พร้อม" อินเตอร์เฟสขั้นสูงเช่นepoll(2)บน Linux จะไม่ทำงานกับไฟล์ปกติ

  • บน Linux มีสายระบบ ( splice(2), vmsplice(2), tee(2)) ที่ทำงานเฉพาะในท่อ [1]

เนื่องจากcatมีการใช้งานมากมันสามารถนำไปใช้เป็นเชลล์ในตัวซึ่งจะหลีกเลี่ยงกระบวนการพิเศษ แต่เมื่อคุณเริ่มต้นบนเส้นทางนั้นสิ่งเดียวกันสามารถทำได้ด้วยคำสั่งส่วนใหญ่ - เปลี่ยนเชลล์เป็นช้า & clunkier หรือperl pythonมันน่าจะดีกว่าถ้าเขียนภาษาสคริปต์อื่นด้วยไวยากรณ์ที่เหมือนท่อง่าย ๆ สำหรับการทำต่อเนื่องแทน ;-)

[1] หากคุณต้องการตัวอย่างง่ายๆไม่ทำขึ้นสำหรับโอกาสที่คุณสามารถดูฉัน "ไบนารี exec จาก stdin" คอมไพล์สรุปสาระสำคัญที่มีคำอธิบายบางอย่างในการแสดงความคิดเห็นที่นี่ การใช้งานcatภายในเพื่อให้ทำงานได้โดยไม่ต้อง UUoC จะทำให้ใหญ่ขึ้น 2 หรือ 3 เท่า


2
ในความเป็นจริง ksh93 จะใช้คำสั่งภายนอกเช่นcatภายใน
jrw32982 รองรับโมนิก้า

3
cat /dev/urandom | cpu_bound_programเรียกใช้การread()เรียกระบบในกระบวนการแยกต่างหาก ตัวอย่างเช่นบน Linux งาน CPU จริงของการสร้างตัวเลขสุ่มมากขึ้น (เมื่อพูลว่างเปล่า) ถูกดำเนินการในการเรียกระบบดังนั้นการใช้กระบวนการแยกต่างหากช่วยให้คุณใช้ประโยชน์จาก CPU core แยกต่างหากเพื่อสร้างข้อมูลสุ่มเป็นอินพุต เช่นในวิธีสร้างไฟล์ข้อความขนาด 1 GB ที่มีตัวเลขสุ่มเป็นวิธีที่เร็วที่สุด
Peter Cordes

4
ที่สำคัญกว่านั้นคือมันlseekไม่ได้ผล cat foo.mp4 | mpv -จะทำงานได้ แต่คุณไม่สามารถค้นหาย้อนหลังได้มากกว่าบัฟเฟอร์แคชของ mpv หรือ mplayer แต่ด้วยการเปลี่ยนเส้นทางอินพุตจากไฟล์คุณสามารถ cat | mpv -เป็นวิธีหนึ่งในการตรวจสอบว่า MP4 มีmoovอะตอมอยู่ที่จุดเริ่มต้นของไฟล์หรือไม่ดังนั้นจึงสามารถเล่นได้โดยไม่ต้องค้นหาจุดสิ้นสุดและด้านหลัง (เช่นถ้าเหมาะสำหรับการสตรีม) มันเป็นเรื่องง่ายที่จะจินตนาการถึงกรณีอื่น ๆ ที่คุณต้องการที่จะทดสอบโปรแกรมสำหรับไฟล์ที่ไม่ seekable โดยทำงานบน/dev/stdinด้วยcatกับการเปลี่ยนเส้นทาง
Peter Cordes

xargs cat | somecmdนี้เป็นจริงมากยิ่งขึ้นเมื่อใช้ หากไฟล์พา ธ ขยายเกินขีด จำกัด บัฟเฟอร์คำสั่งxargsสามารถเรียกใช้catหลายครั้งทำให้เกิดการสตรีมต่อเนื่องในขณะที่การใช้งานxargs somecmdมักจะล้มเหลวโดยตรงเนื่องจากsomecmdไม่สามารถทำงานในทวีคูณเพื่อให้ได้ผลลัพธ์ที่ราบรื่น
tasket

17

เพราะการตรวจจับแมวที่ไร้ประโยชน์นั้นยากมากจริงๆ

ฉันมีเชลล์สคริปต์ที่ฉันเขียน

cat | (somecommand <<!
...
/proc/self/fd/3
...
!) 0<&3

เชลล์สคริปต์ล้มเหลวในการผลิตถ้าถูกลบออกเพราะมันถูกเรียกผ่านcat su -c 'script.sh' someuserความฟุ่มเฟือยที่เห็นได้ชัดนั้นcatทำให้เจ้าของอินพุตมาตรฐานเปลี่ยนไปเป็นผู้ใช้ที่สคริปต์กำลังทำงานอยู่เพื่อที่จะเปิดใหม่อีกครั้งผ่านการ/procทำงาน


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

13

tl; dr:เชลล์ไม่ดำเนินการอัตโนมัติเนื่องจากค่าใช้จ่ายสูงกว่าผลประโยชน์ที่คาดว่าจะเกิดขึ้น

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

  1. ใช้catเป็นบิวด์อินและยังคงรักษาความแตกต่างของไฟล์ v. pipe สิ่งนี้จะช่วยลดค่าใช้จ่ายของผู้บริหารและอาจเป็นทางแยก
  2. ดำเนินการวิเคราะห์ไปป์ไลน์อย่างสมบูรณ์ด้วยความรู้เกี่ยวกับคำสั่งต่าง ๆ ที่ใช้เพื่อดูว่าไฟล์ / ไพพ์สำคัญหรือไม่จากนั้นดำเนินการตามนั้น

ถัดไปคุณต้องพิจารณาต้นทุนและผลประโยชน์ของแต่ละวิธี ประโยชน์ง่ายพอ:

  1. ในกรณีใดกรณีหนึ่งให้หลีกเลี่ยงการ exec (จากcat)
  2. ในกรณีที่สองเมื่อมีความเป็นไปได้ในการทดแทนการเปลี่ยนเส้นทางให้หลีกเลี่ยงการใช้ส้อม
  3. ในกรณีที่คุณต้องใช้ไพพ์บางครั้งอาจเป็นไปได้ที่จะหลีกเลี่ยง fork / vfork แต่ไม่บ่อย นั่นเป็นเพราะความต้องการแมวเทียบเท่าในการทำงานในเวลาเดียวกันกับส่วนที่เหลือของท่อ

ดังนั้นคุณจึงประหยัดเวลาและหน่วยความจำของ CPU เพียงเล็กน้อยโดยเฉพาะถ้าคุณสามารถหลีกเลี่ยงทางแยก แน่นอนว่าคุณจะประหยัดเวลาและหน่วยความจำนี้เฉพาะเมื่อมีการใช้งานคุณสมบัตินี้จริงๆ และคุณประหยัดเวลาในการ fork / exec เท่านั้น ด้วยไฟล์ขนาดใหญ่เวลาส่วนใหญ่เป็นเวลา I / O (เช่นแมวอ่านไฟล์จากดิสก์) ดังนั้นคุณต้องถาม: ใช้บ่อยแค่ไหนcat(ไร้ประโยชน์) ในเชลล์สคริปต์ที่ประสิทธิภาพมีความสำคัญจริง ๆ เปรียบเทียบกับตัวสร้างเชลล์ทั่วไปอื่น ๆ เช่นtest- มันยากที่จะจินตนาการว่าcatใช้ (ไร้ประโยชน์) แม้แต่สิบเท่าบ่อยเท่าที่testใช้ในสถานที่ที่มีความสำคัญ นั่นคือการคาดเดาฉันยังไม่ได้วัดซึ่งเป็นสิ่งที่คุณต้องการทำก่อนที่จะพยายามดำเนินการใด ๆ (หรือในทำนองเดียวกันให้ขอให้บุคคลอื่นนำไปใช้งานเช่นขอคุณสมบัติ)

ถัดไปที่คุณถาม: ค่าใช้จ่ายคืออะไร ค่าใช้จ่ายสองอย่างที่ควรคำนึงถึงคือ (a) รหัสเพิ่มเติมในเชลล์ซึ่งจะเพิ่มขนาดของมัน (และอาจจะเป็นการใช้หน่วยความจำ) จำเป็นต้องมีการบำรุงรักษามากขึ้น และ (b) ความเข้ากันได้ย้อนหลังที่น่าประหลาดใจ POSIX catละเว้นคุณสมบัติมากมายเช่น coreutils ของ GNU catดังนั้นคุณต้องระวังสิ่งที่catbuiltin จะนำมาใช้

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

  2. สำหรับการวิเคราะห์ไปป์ไลน์ฉันไม่คิดว่าเปลือกหอยทำอะไรแบบนี้ในขณะนี้ (บางคนรู้จักจุดจบของไพพ์ไลน์ โดยพื้นฐานแล้วคุณจะต้องเพิ่มเครื่องมือเพิ่มประสิทธิภาพ (ดั้งเดิม) ลงในเชลล์ เครื่องมือเพิ่มประสิทธิภาพมักจะกลายเป็นรหัสที่ซับซ้อนและแหล่งที่มาของข้อบกพร่องมากมาย และข้อผิดพลาดเหล่านั้นอาจเป็นเรื่องที่น่าประหลาดใจการเปลี่ยนแปลงเล็กน้อยในเชลล์สคริปต์อาจทำให้การหลีกเลี่ยงหรือเรียกใช้บั๊กได้

Postscript:คุณสามารถใช้การวิเคราะห์ที่คล้ายกันกับการใช้แมวที่ไร้ประโยชน์ของคุณ ประโยชน์ที่ได้รับ: อ่านง่ายกว่า (แม้ว่า command1 จะใช้ไฟล์เป็นอาร์กิวเมนต์อาจไม่ใช่) ราคา: extra fork และ exec (และถ้า command1 สามารถใช้ไฟล์เป็นอาร์กิวเมนต์อาจทำให้ข้อความแสดงข้อผิดพลาดสับสนมากขึ้น) หากการวิเคราะห์ของคุณบอกให้คุณใช้แมวอย่างไร้ประโยชน์ให้ทำต่อไป


10

catคำสั่งสามารถยอมรับ-เป็นเครื่องหมายสำหรับstdin ( POSIX " หากไฟล์คือ '-' ยูทิลิตี cat จะอ่านจากอินพุตมาตรฐานที่จุดนั้นตามลำดับ ") วิธีนี้ช่วยให้สามารถจัดการไฟล์หรือstdin ได้โดยง่ายซึ่งไม่อนุญาตให้ทำเช่นนี้

พิจารณาทางเลือกทั้งสองเล็กน้อยนี้โดยที่อาร์กิวเมนต์ shell $1คือ-:

cat "$1" | nl    # Works completely transparently
nl < "$1"        # Fails with 'bash: -: No such file or directory'

เวลาที่catมีประโยชน์ก็คือที่ซึ่งมันถูกใช้โดยไม่เจตนาเพื่อรักษาไวยากรณ์ของเชลล์:

file="$1"
reader=cat
[[ $file =~ \.gz$ ]] && reader=zcat
[[ $file =~ \.bz2$ ]] && reader=bzcat
"$reader" "$file"

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

cat file.txt

ในสถานการณ์อื่นใดcatจำเป็นต้องมีoroperties ของตัวเอง


6

คำสั่ง cat สามารถทำสิ่งที่เชลล์ไม่สามารถทำได้ (หรืออย่างน้อยก็ทำไม่ได้ง่าย) ตัวอย่างเช่นสมมติว่าคุณต้องการพิมพ์อักขระที่อาจมองไม่เห็นเช่นแท็บการขึ้นบรรทัดใหม่หรือการขึ้นบรรทัดใหม่ * อาจเป็นวิธีที่ทำได้โดยใช้คำสั่ง shell builtin เท่านั้น แต่ฉันไม่สามารถนึกถึงส่วนบนสุดของหัวของฉันได้ รุ่น GNU ของแมวสามารถทำได้ด้วย-Aอาร์กิวเมนต์หรือ-v -E -Tอาร์กิวเมนต์ (ฉันไม่รู้เกี่ยวกับ cat รุ่นอื่น) คุณสามารถนำหน้าแต่ละบรรทัดด้วยหมายเลขบรรทัดโดยใช้-n(อีกครั้ง IDK ถ้าไม่ใช่รุ่น GNU สามารถทำได้)

ข้อดีอีกอย่างของ cat คือสามารถอ่านไฟล์ได้หลายไฟล์ ในการทำเช่นนั้นสามารถพิมพ์ได้เพียงอย่างเดียวcat file1 file2 file3เดียว เมื่อต้องการทำเช่นเดียวกันกับเปลือกหอยสิ่งต่าง ๆ อาจจะยุ่งยากแม้ว่าการวนรอบที่สร้างขึ้นอย่างระมัดระวังอาจจะได้ผลลัพธ์ที่เหมือนกัน ที่กล่าวว่าคุณต้องการที่จะใช้เวลาในการเขียนวนเช่นนี้หรือไม่เมื่อมีทางเลือกง่าย ๆ อยู่? ฉันไม่!

การอ่านไฟล์ที่มี cat น่าจะใช้ CPU น้อยกว่าเชลล์เนื่องจาก cat เป็นโปรแกรมที่คอมไพล์ล่วงหน้า เมื่ออ่านกลุ่มไฟล์ขนาดใหญ่สิ่งนี้อาจปรากฏชัดเจน แต่ฉันไม่เคยทำเช่นนั้นบนเครื่องของฉันดังนั้นฉันจึงไม่แน่ใจ

คำสั่ง cat ยังมีประโยชน์ในการบังคับให้คำสั่งยอมรับอินพุตมาตรฐานในอินสแตนซ์ที่อาจไม่ พิจารณาสิ่งต่อไปนี้:

echo 8 | sleep

หมายเลข "8" จะไม่ได้รับการยอมรับจากคำสั่ง "sleep" เนื่องจากไม่ได้หมายถึงการยอมรับอินพุตมาตรฐานจริงๆ ดังนั้นการนอนหลับจะไม่สนใจข้อมูลนั้นบ่นเกี่ยวกับการไม่มีข้อโต้แย้งและออก อย่างไรก็ตามหากมีหนึ่งประเภท:

echo 8 | sleep $(cat)

กระสุนจำนวนมากจะขยายไปถึงนี้sleep 8และโหมดสลีปจะรอ 8 วินาทีก่อนออก คุณสามารถทำสิ่งที่คล้ายกับ ssh:

command | ssh 1.2.3.4 'cat >> example-file'

คำสั่งนี้พร้อมผนวกไฟล์ตัวอย่างบนเครื่องที่มีที่อยู่ 1.2.3.4 ด้วยสิ่งใดก็ตามที่เอาต์พุตจาก "คำสั่ง"

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


ฉันจะจบประโยคสุดท้ายโดย "ไม่เป็นไปได้อย่างง่ายดาย"
Basile Starynkevitch

3

โปรดจำไว้ว่าผู้ใช้อาจมีcatใน$PATHซึ่งไม่ใช่ POSIX อย่างแน่นอนcat(แต่อาจมีตัวแปรบางตัวที่สามารถบันทึกบางอย่าง) ในกรณีนี้คุณไม่ต้องการให้เชลล์ลบออก

PATH อาจมีการเปลี่ยนแปลงแบบไดนามิกและจากนั้นcat ไม่ได้เป็นสิ่งที่คุณเชื่อว่ามันเป็น การเขียนเชลล์จะเป็นการยากที่จะเพิ่มประสิทธิภาพที่คุณฝันไว้

นอกจากนี้ในทางปฏิบัติ cat มันเป็นโปรแกรมที่ค่อนข้างเร็ว มีเหตุผลเชิงปฏิบัติเล็กน้อย (ยกเว้นสุนทรียศาสตร์) เพื่อหลีกเลี่ยง

ดูการแยกวิเคราะห์ POSIX ที่ยอดเยี่ยมด้วยคุยกันโดย Hell Yann Regis-Gianas ที่ FOSDEM2018 มันให้เหตุผลที่ดีอื่น ๆ เพื่อหลีกเลี่ยงการพยายามทำสิ่งที่คุณฝันไว้ในเปลือกหอย

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

ในทางปฏิบัติการเขียนใหม่เพื่อปรับปรุงประสิทธิภาพสคริปต์เชลล์ขนาดเล็ก (ไม่ร้อยบรรทัด) ในภาษาสคริปต์ที่ดีกว่า (Python, AWK, Guile, ... ) โดยทั่วไปแล้ว และมันก็ไม่สมเหตุสมผล (ด้วยเหตุผลทางวิศวกรรมซอฟต์แวร์มากมาย) ในการเขียนเชลล์สคริปขนาดใหญ่: เมื่อคุณเขียนเชลล์สคริปเกินกว่าหนึ่งร้อยบรรทัดคุณจะต้องพิจารณาการเขียนใหม่สคริปท์ (แม้สำหรับเหตุผลในการอ่านและการบำรุงรักษา) : ในฐานะที่เป็นภาษาการเขียนโปรแกรมเชลล์นั้นแย่มาก อย่างไรก็ตามมีเชลล์สคริปต์ที่สร้างขึ้นจำนวนมากและด้วยเหตุผลที่ดี (เช่น GNU autoconf ที่สร้างขึ้นconfigureสคริปต์ที่ )

เกี่ยวกับไฟล์ต้นฉบับเดิมขนาดใหญ่ผ่านพวกเขาจะcatเป็นคนเดียวอาร์กิวเมนต์ไม่ปฏิบัติที่ดีและ sysadmins ส่วนใหญ่รู้ว่า (เมื่อเชลล์สคริปต์ใด ๆ ต้องใช้เวลามากกว่านาทีไปวิ่งที่คุณจะเริ่มพิจารณาการเพิ่มประสิทธิภาพของมัน) สำหรับไฟล์กิกะไบต์ขนาดใหญ่catจะไม่เป็นเครื่องมือที่ดีในการประมวลผล


3
"เหตุผลในทางปฏิบัติไม่กี่ค่อนข้างจะหลีกเลี่ยงได้" - ทุกคนที่รอคอยcat some-huge-log | tail -n 5ที่จะวิ่ง (ที่tail -n 5 some-huge-logสามารถกระโดดตรงไปยังจุดสิ้นสุดในขณะที่catอ่านเพียงด้านหน้าไปด้านหลัง) จะไม่เห็นด้วย
Charles Duffy

ความคิดเห็นเช็คเอาท์ ^ cating ไฟล์ข้อความขนาดใหญ่ในช่วงสิบ GB (ซึ่งสร้างขึ้นสำหรับการทดสอบ) ใช้เวลานานมาก จะไม่แนะนำ
Sergiy Kolodyazhnyy

1
BTW, re: "ไม่มีตลาดสำคัญสำหรับการปรับแต่งเชลล์" - ksh93 เป็นเชลล์ที่ปรับให้เหมาะสมและเป็นเชลล์ที่ดีมาก มันถูก , ในขณะที่ขายประสบความสำเร็จเป็นผลิตภัณฑ์เชิงพาณิชย์ (น่าเศร้าที่การได้รับใบอนุญาตในเชิงพาณิชย์ยังทำให้มันเพียงพอที่โคลนที่เขียนไม่ดีและผู้สืบทอดอื่น ๆ ที่มีความสามารถน้อย แต่ฟรี แต่เสียค่าใช้จ่ายได้เข้ามาอยู่ในโลกภายนอกไซต์เหล่านั้นที่เต็มใจจ่ายใบอนุญาต มีวันนี้)
Charles Duffy

(ไม่ได้ใช้เทคนิคเฉพาะที่คุณจดบันทึก แต่ตรงไปตรงมาเทคนิคเหล่านั้นไม่สมเหตุสมผลเมื่อใช้แบบจำลองกระบวนการเทคนิคที่ใช้จะดีใช้ดีและมีผลดี )
Charles Duffy

2

การเพิ่ม @Kusalananda คำตอบ (และความคิดเห็น @alephzero) แมวอาจเป็นอะไรก็ได้:

alias cat='gcc -c'
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

หรือ

echo 'echo 1' > /usr/bin/cat
cat "$MYFILE" | command1 | command2 > "$OUTPUT"

ไม่มีเหตุผลว่า cat (ด้วยตัวเอง) หรือ / usr / bin / cat บนระบบคือ cat ที่เป็นเครื่องมือ concatenate


3
นอกเหนือจากพฤติกรรมของcatถูกกำหนดโดย POSIX และไม่ควรแตกต่างกันอย่างดุเดือด
roaima

2
@roaima: PATH=/home/Joshua/bin:$PATH cat ...คุณแน่ใจหรือcatไม่ว่าตอนนี้คุณรู้อะไรบ้าง
Joshua

1
@ โจชัวมันไม่สำคัญจริงๆ เราทั้งคู่รู้ว่าcatสามารถถูกแทนที่ได้ แต่เราทั้งคู่ก็รู้ว่าไม่ควรถูกแทนที่ด้วยอย่างอื่นอย่างซุกซน ความคิดเห็นของฉันชี้ให้เห็นว่า POSIX ได้รับคำสั่งพฤติกรรมเฉพาะ (ชุดย่อย) ที่คาดว่าจะมีอยู่อย่างสมเหตุสมผล บางครั้งฉันได้เขียนเชลล์สคริปต์ที่ขยายพฤติกรรมของยูทิลิตี้มาตรฐาน ในกรณีนี้เชลล์สคริปต์ทำงานและทำงานเหมือนกับเครื่องมือที่ถูกแทนที่ยกเว้นว่ามันมีความสามารถเพิ่มเติม
roaima

@Joshua: บนแพลตฟอร์มส่วนใหญ่เชลล์รู้ (หรืออาจรู้) ซึ่งไดเร็กทอรีเก็บไฟล์ปฏิบัติการที่ใช้คำสั่ง POSIX ดังนั้นคุณสามารถเลื่อนการทดแทนจนกระทั่งหลังจากการขยายนามแฝงและการแก้ปัญหาเส้นทางและทำเพื่อ/bin/catเท่านั้น (และคุณต้องการให้มันเป็นตัวเลือกที่คุณสามารถปิดได้) หรือคุณจะสร้างcatเชลล์ในตัว (ซึ่งอาจกลับไปใช้/bin/catกับ args หลายอัน?) เพื่อให้ผู้ใช้สามารถควบคุมได้ว่าพวกเขาต้องการเวอร์ชั่นภายนอกเป็นปกติหรือไม่ enable catวิธีด้วย killเหมือน (ฉันคิดว่าทุบตีcommand catจะทำงาน แต่ไม่ข้าม builtins)
Peter Cordes

ถ้าคุณให้นามแฝงเปลือกจะรู้ว่าในสภาพแวดล้อมที่ไม่ได้หมายถึงปกติcat catเห็นได้ชัดว่าการเพิ่มประสิทธิภาพควรจะดำเนินการหลังจากนามแฝงได้รับการประมวลผล ฉันพิจารณาตัวบิวด์อินเพื่อแสดงคำสั่งในไดเรกทอรีเสมือนที่มีอยู่ในพา ธ ของคุณเสมอ หากคุณต้องการหลีกเลี่ยงเวอร์ชันในตัวของคำสั่งใด ๆ (เช่นtest) คุณต้องใช้ชุดตัวเลือกที่มีพา ธ
Mikko Rantalainen

1

การใช้ "ไร้ประโยชน์" สองอย่างสำหรับแมว:

sort file.txt | cat header.txt - footer.txt | less

... ที่นี่catใช้ในการผสมไฟล์และอินพุตแบบไพพ์

find . -name '*.info' -type f | sh -c 'xargs cat' | sort

... ที่นี่xargsสามารถยอมรับชื่อไฟล์จำนวนนับไม่ถ้วนและรันได้catบ่อยเท่าที่ต้องการในขณะที่ทำให้มันทำงานเหมือนสตรีมเดียว ดังนั้นจึงเหมาะกับรายการไฟล์ขนาดใหญ่ที่การใช้งานโดยตรงxargs sortไม่ได้


ทั้งสองกรณีการใช้งานเหล่านี้จะหลีกเลี่ยงได้เล็กน้อยโดยการสร้างเชลล์ในตัวเฉพาะขั้นตอนหากcatเรียกใช้ด้วยอาร์กิวเมนต์หนึ่งตัว โดยเฉพาะอย่างยิ่งกรณีที่shส่งผ่านสตริงและxargsจะโทรcatโดยตรงไม่มีวิธีที่เชลล์สามารถใช้งานได้ในตัว
Mikko Rantalainen

0

นอกเหนือจากสิ่งอื่น ๆ - การcatตรวจสอบจะเพิ่มค่าใช้จ่ายเพิ่มเติมและความสับสนเกี่ยวกับการใช้งานที่catไม่มีประโยชน์จริง ๆ IMHO เนื่องจากการตรวจสอบดังกล่าวไม่มีประสิทธิภาพและสร้างปัญหากับcatการใช้งานที่ถูกกฎหมาย

เมื่อคำสั่งจัดการกับสตรีมมาตรฐานพวกเขาจะต้องใส่ใจกับการอ่าน / เขียนไปยังตัวอธิบายไฟล์มาตรฐาน คำสั่งสามารถรู้ได้ว่า stdin สามารถค้นหาได้/ lseekableหรือไม่ซึ่งระบุว่าไปป์หรือไฟล์

หากเราเพิ่มลงในการตรวจสอบแบบผสมว่ากระบวนการใดมีเนื้อหา stdin จริง ๆ เราจะต้องค้นหากระบวนการในอีกด้านหนึ่งของไพพ์ สิ่งนี้สามารถทำได้ในแง่ของเชลล์เองดังแสดงในSuperUserโพสต์โดย Kyle Jones และในแง่ของเชลล์

(find /proc -type l | xargs ls -l | fgrep 'pipe:[20043922]') 2>/dev/null

ตามที่แสดงในโพสต์ที่เชื่อมโยง นี่คือ 3 คำสั่งเพิ่มเติม (ดังนั้นfork()s และ s พิเศษexec()) และ traversals แบบเรียกซ้ำ (ทั้งล็อตreaddir()โทรทั้งหมด)

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

# adding header and footer to file
( cmd; cat file; cmd ) | cmd
# tr command does not accept files as arguments
cat log1 log2 log3 | tr '[:upper:]' '[:lower:]'

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

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