คำสั่งไปยังเนื้อหาไฟล์ที่ส่งออกไปยัง stdout?


28

ฉันรู้ว่าcatสามารถทำสิ่งนี้ได้ แต่จุดประสงค์หลักของมันคือการต่อข้อมูลแทนที่จะแสดงเนื้อหา

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


1
ที่จริงแล้ว 99% ของจำนวนครั้งที่คุณใช้ cat คือการแสดงไฟล์แทนที่จะต่ออะไรก็ได้
LatinSuD

1
@LatinSuD - 100% ของเวลา - catต่อกัน
mikeserv

@mikeserv: มันเป็นเรื่องของสิ่งที่คุณหมายถึงโดยการดำเนินการ การใช้ภาษาปกติจะหมายถึงการกระทำอย่างน้อยหนึ่งครั้ง "การพิมพ์" สตริงว่างไม่เกี่ยวข้องกับการพิมพ์อักขระใด ๆ มันยุติธรรมที่จะบอกว่ามันไม่ได้พิมพ์อะไร ขณะนี้การคำนวณa+b+cเกี่ยวข้องกับการเพิ่ม 2 ส่วนและการคำนวณaไม่เกี่ยวข้องกับการเพิ่มอะไรเลย การดำเนินการในทำนองเดียวกันcat fไม่เกี่ยวข้องกับการต่อข้อมูล (แม้ว่านี่จะเป็นสิ่งเดียวที่ "การเรียงลำดับของไฟล์เดียว" อาจหมายถึง)
Marc van Leeuwen

2
@mikeserv: ฉันไม่ได้รับสิ่งที่คุณหมายถึงโดยใน + ออก แน่นอนว่าcatอ่านไฟล์และเขียนไฟล์อื่น (สตรีม) แต่นั่นไม่ได้หมายความว่ามันจะต่ออะไรเลย หากคุณอาจหมายถึงว่าcat fกำลังทำอยู่cat - fนั่นไม่จริง
Marc van Leeuwen

1
@ สับสน00: catหมายถึงการเชื่อมกัน ( cat file1 file2จะเชื่อมทั้งสองไฟล์เข้ากับ stdout) แต่ผลข้างเคียงคือเมื่อมีเพียง 1 ไฟล์เป็นอาร์กิวเมนต์มันจะเอาท์พุทหนึ่งไฟล์ไปยัง stdout (ไม่ว่าจะไปที่ใดเทอร์มินัลของคุณหรือเปลี่ยนเส้นทางไปยังบางสิ่ง) ดังนั้นจึงไม่มีคำสั่งอื่นที่สร้างขึ้นเพียงเพื่อส่งออกใน stdout ตามที่catมีอยู่และได้รับอนุญาตเพียง catดังนั้นคุณต้องการใช้
Olivier Dulac

คำตอบ:


37

catที่เห็นได้ชัดที่สุดคือหนึ่ง แต่ยังมีลักษณะที่และhead tailนอกจากนี้ยังมี utillities เปลือกอื่น ๆ sedที่จะพิมพ์บรรทัดแฟ้มโดยบรรทัด: awk, grep, แต่สิ่งเหล่านี้คือการสลับเนื้อหาของไฟล์หรือเพื่อค้นหาภายในไฟล์

ฉันทำการทดสอบสองสามครั้งเพื่อประเมินว่าแบบใดมีประสิทธิภาพมากที่สุด ฉันเรียกใช้รางทั้งหมดstraceเพื่อดูว่าระบบใดเรียกระบบน้อยที่สุด ไฟล์ของฉันมี 1275 บรรทัด

  • awk: การเรียกใช้ระบบ 1355
  • cat: ระบบ 51 สาย
  • grep: การเรียกใช้ระบบ 1337
  • head: การเรียกระบบ 93 ครั้ง
  • tail: การเรียกระบบ 130 ครั้ง
  • sed: การโทรของระบบ 1378

อย่างที่คุณเห็นแม้ว่าcatถูกออกแบบมาเพื่อเชื่อมไฟล์เข้าด้วยกันมันเป็นไฟล์ที่เร็วและมีประสิทธิภาพมากที่สุด sed, awkและgrepพิมพ์บรรทัดไฟล์โดยสายที่ว่าทำไมพวกเขามีมากว่า 1,275 สายระบบ


8
เป็นความคิดที่ดีที่จะนับ syscalls!
Jan

1
+1 คำตอบมีความแม่นยำมากขึ้น (เกี่ยวกับความหมายของแมว) สมบูรณ์มากขึ้น (ทางเลือก) และวิจัย (syscalls)
Olivier Dulac

23

ฉันรู้ว่าcatสามารถทำสิ่งนี้ได้ แต่จุดประสงค์หลักของมันคือการต่อข้อมูลแทนที่จะแสดงเนื้อหา

วัตถุประสงค์ของcatมันคือการอ่านไฟล์และส่งออกไปยัง stdout


1
แต่cat --helpพูดว่า "Concatenate FILE (s) หรืออินพุตมาตรฐานไปยังเอาต์พุตมาตรฐาน" ฉันไม่ต้องการที่จะต่อสิ่งใดเข้าด้วยกัน
สับสน

18
เชื่อหรือไม่ว่าแมวเป็นสิ่งที่คุณต้องการอยู่แล้ว
Jan

5
ไม่ @ สับสน00, ม.ค. ถูกต้อง สิ่งคือ - เทอร์มินัลคือ stdout - คุณเห็น? ทำreadlink /dev/fd/1เช่น - คุณควรได้รับชื่อ tty ของคุณในนั้นถ้าทำงานที่พร้อมท์มาตรฐาน ดังนั้นการเชื่อมข้อมูลเข้ากับเอาต์พุตจึงเป็นสิ่งที่คุณขอให้ทำ
mikeserv

2
@ mikeserv ใช่ฉันเห็นจุดของคุณฉันเดาว่าฉันคงที่เกินไปกับความหมาย 'concatenate'
สับสน

3
เหตุผลคือการพิมพ์เนื้อหาของไฟล์เดียวเป็นเพียงกรณีพิเศษของการพิมพ์เนื้อหาของไฟล์หนึ่งไฟล์หรือมากกว่านั้นตามลำดับ
zwol

10

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

หากวัตถุประสงค์ของคุณจะส่งสิ่งที่เขียนไปออกมาตรฐานเข้าไปในท่อแล้วใช้catจะมีสิทธิ์ที่จะใช้ประโยชน์ของรางวัลแมวตั้งแต่cat file | pipeline(ที่pipelineย่อมาจากท่อใด ๆ ) <file pipelineสามารถทำได้อย่างมีประสิทธิภาพมากขึ้นเป็น แต่อีกครั้งจากถ้อยคำของคุณฉันอนุมานว่านี่ไม่ใช่ความตั้งใจของคุณ

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

หากมีโปรแกรมnullที่ไม่มีข้อโต้แย้งและเพียงแค่คัดลอกอินพุตมาตรฐานไปยังเอาต์พุตมาตรฐาน (วัตถุที่เป็นกลางสำหรับไพพ์ไลน์) คุณสามารถทำสิ่งที่คุณต้องการ<file nullได้ ไม่มีโปรแกรมดังกล่าวแม้ว่ามันจะง่ายต่อการเขียน (โปรแกรม C ที่มีเพียงmainฟังก์ชั่นหนึ่งบรรทัดสามารถทำงานได้) แต่การโทรcatโดยไม่มีข้อโต้แย้ง (หรือcat -หากคุณต้องการชัดเจน) ก็เป็นเช่นนั้น

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

เมื่อคุณประสบความสำเร็จในการเขียนnocatโปรแกรมทำไมหยุดที่อาร์กิวเมนต์เดียว? การห่อโค้ดลงในลูปfor(;*argp!=NULL;++argp)นั้นไม่ได้ใช้ความพยายามเลยเพิ่มคำสั่งของเครื่องลงไปในไบนารีมากที่สุดและหลีกเลี่ยงการบ่นเกี่ยวกับจำนวนอาร์กิวเมนต์ที่ไม่ถูกต้อง Voilàเวอร์ชันดั้งเดิมของcatไฟล์ที่ต่อกัน (พูดตามตรงคุณต้องปรับแต่งนิดหน่อยเพื่อที่จะได้ไม่มีข้อโต้แย้งnull)

แน่นอนในcatโปรแกรมจริงพวกเขาเพิ่ม bells and whistles ไม่กี่เพราะพวกเขามักจะทำ แต่สาระสำคัญก็คือแง่มุม "การต่อข้อมูล" ของcatค่าใช้จ่ายนั้นไม่มีความพยายามใด ๆ เลยทั้งสำหรับโปรแกรมเมอร์และสำหรับเครื่องที่ใช้งานมัน ความจริงที่ว่าcatsubsumes nullและnocatอธิบายถึงการไม่มีอยู่ของโปรแกรมดังกล่าว หลีกเลี่ยงการใช้catอาร์กิวเมนต์เดียวหากผลลัพธ์เข้าสู่ไพพ์ไลน์ แต่ถ้าใช้เพื่อแสดงเนื้อหาไฟล์บนเทอร์มินัลแม้แต่หน้าเว็บที่ฉันเชื่อมโยงเพื่อยอมรับว่านี่เป็นการใช้งานที่มีประโยชน์catดังนั้นอย่าลังเลเลย


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


7

ภายใต้ความzshพยายาม

<file

ฉันเชื่อว่าเป็นวิธีที่สั้นที่สุดในการพิมพ์ไฟล์ มันใช้ 'hidden' cat(หรือmoreถ้า stdout เป็นเทอร์มินัล) แต่คำสั่งที่ใช้สำหรับการพิมพ์ถูกควบคุมโดยREADNULLCMDตัวแปรซึ่งคุณสามารถเขียนทับได้อย่างปลอดภัยโดยตรงโดยใช้ชื่อคำสั่งหรือแม้แต่บางฟังก์ชัน ตัวอย่างเช่นการพิมพ์ไฟล์ที่มีหมายเลขบรรทัด:

numcat() { nl -s'> ' -w2 - }
READNULLCMD=numcat
<file

5

POSIX กำหนดcatเป็น:

ชื่อ

cat - เชื่อมต่อและพิมพ์ไฟล์

สรุป

cat [-u] [ไฟล์ ... ]

รายละเอียด

ยูทิลิตี้ cat จะต้องอ่านไฟล์ตามลำดับและจะเขียนเนื้อหาลงในเอาต์พุตมาตรฐานในลำดับเดียวกัน

ดังนั้นผมจึงคิดว่าconcatenateที่นี่หมายถึงการอ่านไฟล์ในลำดับ


5

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

printf '%s' "$(<file.txt)"

4
นอกเหนือจากการเป็นวงเวียนโดยเฉพาะคำสั่งที่แสดงจะไม่เทียบเท่าcat file.txtเนื่องจากมันจะลบบรรทัดใหม่ที่ต่อท้าย (สิ่ง$(...)นี้ทำ)
Marc van Leeuwen

+1 น่ารักดี ไม่ทราบว่า
James M. Lay

3

การใช้bashbuiltins และหลีกเลี่ยงการสร้างกระบวนการย่อย:

{ while IFS='' read -rd '' _bcat_; do printf '%s\0' "${_bcat_}"; done; printf '%s' "${_bcat_}"; unset _bcat_; } <'/path/to/file'

IFSจะใช้กับreadคำสั่งเท่านั้นดังนั้นไม่ต้องกังวลกับIFSการเปลี่ยนแปลงทั่วโลกของคุณ

วนซ้ำจำเป็นต้องใช้เพื่อจัดการอักขระว่าง (ขอบคุณStéphane Chazelas)

วิธีนี้ไม่เหมาะสำหรับไฟล์ขนาดใหญ่เนื่องจากเนื้อหาไฟล์อ่านไปยังตัวแปร (ดังนั้นหน่วยความจำ) ก่อน BTW ฉันพยายามพิมพ์ไฟล์ข้อความ 39M ด้วยวิธีนี้และการใช้หน่วยความจำ bash ไม่เกิน 5M ดังนั้นไม่แน่ใจเกี่ยวกับกรณีนี้

มันยังช้าและไม่มีประสิทธิภาพของ CPU: สำหรับไฟล์ 39M เดียวกันนั้นใช้เวลา 3 นาทีในการใช้งานคอร์ 100%

สำหรับไฟล์หรือไบนารีขนาดใหญ่ควรใช้cat '/path/to/file'หรือดีกว่าdd if='/path/to/file' bs=1Mถ้าเป็นไปได้


1
ดูpv -qว่าบนลีนุกซ์ใดที่สามารถใช้splice()stdin / stdout บางประเภทจะปรับปรุงประสิทธิภาพ
Stéphane Chazelas

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