มีหลายสิ่งที่ต้องพิจารณาที่นี่
i=`cat input`
อาจมีราคาแพงและมีการเปลี่ยนแปลงมากมายระหว่างเชลล์
นั่นเป็นคุณสมบัติที่เรียกว่าการทดแทนคำสั่ง แนวคิดคือการเก็บเอาต์พุตทั้งหมดของคำสั่งลบอักขระบรรทัดใหม่ต่อท้ายลงในiตัวแปรในหน่วยความจำ
ในการทำเช่นนั้นเชลล์แยกคำสั่งในเชลล์ย่อยและอ่านเอาต์พุตผ่านไพพ์หรือซ็อกเก็ตคู่ คุณเห็นการเปลี่ยนแปลงมากมายที่นี่ ในไฟล์ 50MiB นี่ผมสามารถมองเห็นเช่นทุบตีเป็นครั้งที่ 6 เป็นช้าเป็น ksh93 แต่เร็วขึ้นเล็กน้อยกว่า zsh yashและรวดเร็วเป็นสองเท่า
เหตุผลหลักสำหรับbashการช้าคือมันอ่านจากท่อ 128 ไบต์ในแต่ละครั้ง (ในขณะที่กระสุนอื่น ๆ อ่าน 4KiB หรือ 8KiB ในเวลา) และถูกลงโทษโดยการเรียกใช้ระบบ
zshจำเป็นต้องทำการโพสต์ - โพรเซสซิงเพื่อหลีกเลี่ยง NUL ไบต์ (เชลล์อื่นแบ่งเป็น NUL ไบต์) และyashทำการประมวลผลที่หนักยิ่งขึ้นโดยการแยกวิเคราะห์อักขระหลายไบต์
เชลล์ทั้งหมดจำเป็นต้องตัดอักขระบรรทัดใหม่ที่ต่อท้ายซึ่งอาจทำงานได้อย่างมีประสิทธิภาพมากหรือน้อย
บางคนอาจต้องการที่จะจัดการกับ NUL ไบต์อย่างสง่างามกว่าคนอื่น ๆ และตรวจสอบการปรากฏตัวของพวกเขา
จากนั้นเมื่อคุณมีตัวแปรขนาดใหญ่ในหน่วยความจำการจัดการใด ๆ ที่เกี่ยวข้องกับการจัดสรรหน่วยความจำเพิ่มเติมและการจัดการข้อมูลข้าม
ที่นี่คุณผ่าน (กำลังตั้งใจที่จะผ่าน) echoเนื้อหาของตัวแปรไป
โชคดีที่echoมีอยู่แล้วในเชลล์ของคุณมิฉะนั้นการประมวลผลอาจล้มเหลวด้วยรายการข้อผิดพลาดที่ยาวเกินไป แม้กระนั้นการสร้างอาร์เรย์รายการอาร์กิวเมนต์อาจเกี่ยวข้องกับการคัดลอกเนื้อหาของตัวแปร
ปัญหาหลักอื่น ๆ ในแนวทางการทดแทนคำสั่งของคุณคือคุณกำลังเรียกใช้ตัวดำเนินการแยก + glob (โดยลืมอ้างถึงตัวแปร)
สำหรับสิ่งนั้นเชลล์จำเป็นต้องใช้สตริงเป็นสตริงของอักขระ (แม้ว่าเชลล์บางตัวจะทำไม่ได้และเป็นบั๊กกี้ในเรื่องนั้น) ดังนั้นในโลแคล UTF-8 นั่นหมายถึงการแยกลำดับ UTF-8 (ถ้าไม่ได้ทำไปแล้วyash) ค้นหา$IFSอักขระในสตริง หาก$IFSมีช่องว่างแท็บหรือขึ้นบรรทัดใหม่ (ซึ่งเป็นกรณีโดยค่าเริ่มต้น) อัลกอริทึมจะซับซ้อนและมีราคาแพงยิ่งขึ้น จากนั้นคำที่เกิดจากการแยกจะต้องได้รับการจัดสรรและคัดลอก
ส่วน glob จะมีราคาแพงกว่า ถ้าใด ๆ ของคำเหล่านั้นประกอบด้วยอักขระ glob ( *, ?, [) แล้วเปลือกจะมีการอ่านเนื้อหาของไดเรกทอรีบางและทำบางจับคู่รูปแบบที่มีราคาแพง ( bashของการดำเนินงานเช่นเป็นฉาวโฉ่ที่เลวร้ายมากที่นั้น)
หากอินพุตมีบางสิ่งบางอย่างเช่น/*/*/*/../../../*/*/*/../../../*/*/*นั้นจะมีราคาแพงมากเพราะหมายถึงการระบุไดเรกทอรีหลายพันรายการและสามารถขยายไปหลายร้อย MiB
จากนั้นechoโดยทั่วไปจะทำการประมวลผลพิเศษบางอย่าง การใช้งานบางอย่างจะขยาย\xลำดับในอาร์กิวเมนต์ที่ได้รับซึ่งหมายถึงการแยกวิเคราะห์เนื้อหาและอาจเป็นการจัดสรรและคัดลอกข้อมูลอีกครั้ง
ในอีกทางหนึ่งตกลงในเชลล์ส่วนใหญ่catไม่ได้มีในตัวดังนั้นจึงหมายถึงการฟอร์กกระบวนการและดำเนินการ (เพื่อโหลดรหัสและไลบรารี) แต่หลังจากการเรียกใช้ครั้งแรกรหัสนั้นและเนื้อหาของไฟล์อินพุต จะถูกแคชในหน่วยความจำ ในทางกลับกันจะไม่มีคนกลาง catจะอ่านจำนวนมากในแต่ละครั้งและเขียนทันทีโดยไม่ต้องดำเนินการและไม่จำเป็นต้องจัดสรรหน่วยความจำจำนวนมากเพียงบัฟเฟอร์เดียวที่มันนำมาใช้ใหม่
นอกจากนี้ยังหมายความว่ามันมีความน่าเชื่อถือมากขึ้นเพราะไม่สำลักกับไบต์ NUL และไม่ตัดอักขระขึ้นบรรทัดใหม่ (และไม่แบ่ง + glob แม้ว่าคุณสามารถหลีกเลี่ยงปัญหานี้ได้ด้วยการอ้างอิงตัวแปรและไม่ ขยายลำดับ escape แม้ว่าคุณสามารถหลีกเลี่ยงได้โดยใช้printfแทนecho)
ถ้าคุณต้องการที่จะเพิ่มประสิทธิภาพของมันต่อไปแทนการกล่าวอ้างcatหลายครั้งเพียงแค่ผ่านinputหลาย ๆ catครั้งเพื่อ
yes input | head -n 100 | xargs cat
จะเรียกใช้ 3 คำสั่งแทน 100
เพื่อให้รุ่นตัวแปรเชื่อถือได้มากขึ้นคุณจะต้องใช้zsh(เชลล์อื่นไม่สามารถรับมือกับ NUL ไบต์) และทำมัน:
zmodload zsh/mapfile
var=$mapfile[input]
repeat 10 print -rn -- "$var"
หากคุณรู้ว่าอินพุตไม่มีไบต์ NUL แสดงว่าคุณสามารถทำ POSIXly ได้อย่างน่าเชื่อถือ (แม้ว่ามันอาจจะไม่ทำงานในที่ที่printfไม่มีบิวด์) ด้วย:
i=$(cat input && echo .) || exit # add an extra .\n to avoid trimming newlines
i=${i%.} # remove that trailing dot (the \n was removed by cmdsubst)
n=10
while [ "$n" -gt 10 ]; do
printf %s "$i"
n=$((n - 1))
done
แต่นั่นจะไม่มีประสิทธิภาพมากกว่าการใช้catลูป (เว้นแต่ว่าอินพุตจะเล็กมาก)
cat $(for i in $(seq 1 10); do echo "input"; done) >> outputอะไร? :)