ถ้าคุณต้องเก็บเอาท์พุทและคุณต้องการอาเรmapfileย์
ก่อนอื่นให้พิจารณาว่าคุณต้องการจัดเก็บเอาต์พุตของคำสั่งของคุณหรือไม่ หากคุณไม่ต้องการให้รันคำสั่ง
หากคุณตัดสินใจว่าคุณต้องการอ่านเอาต์พุตของคำสั่งเป็นอาร์เรย์ของบรรทัดมันเป็นความจริงที่วิธีหนึ่งที่จะทำได้คือปิดการใช้งาน globbing ตั้งค่าIFSให้แยกบรรทัดใช้การทดแทนคำสั่งภายใน( )ไวยากรณ์การสร้างอาร์เรย์และรีเซ็ตIFSภายหลัง ซึ่งซับซ้อนกว่าที่คิด คำตอบของ terdonครอบคลุมบางส่วนของวิธีการนั้น แต่ฉันขอแนะนำให้คุณใช้คำสั่งในตัวmapfileของ Bash shell สำหรับอ่านข้อความเป็นอาร์เรย์ของบรรทัดแทน นี่เป็นกรณีง่ายๆที่คุณอ่านจากไฟล์:
mapfile < filename
MAPFILEนี้อ่านบรรทัดลงในอาร์เรย์ที่เรียกว่า โดยไม่ต้องเปลี่ยนเส้นทางการป้อนข้อมูล ที่คุณจะได้รับการอ่านจากเปลือกเข้ามาตรฐาน (ปกติ terminal ของคุณ) แทนของแฟ้มการตั้งชื่อโดย หากต้องการอ่านอาเรย์อื่นนอกเหนือจากค่าเริ่มต้นให้ส่งชื่อของมัน ตัวอย่างเช่นสิ่งนี้อ่านในอาร์เรย์:< filenamefilenameMAPFILElines
mapfile lines < filename
พฤติกรรมเริ่มต้นอื่นที่คุณอาจเลือกที่จะเปลี่ยนแปลงคืออักขระบรรทัดใหม่ที่ท้ายบรรทัดจะถูกแทนที่ มันจะปรากฏเป็นอักขระตัวสุดท้ายในแต่ละองค์ประกอบอาร์เรย์ (เว้นแต่ว่าอินพุตจะไม่มีอักขระขึ้นบรรทัดใหม่ซึ่งในกรณีนั้นองค์ประกอบสุดท้ายจะไม่มีหนึ่ง) การชอมป์บรรทัดใหม่เหล่านี้เพื่อให้พวกเขาไม่ได้ปรากฏในอาร์เรย์ผ่านตัวเลือกในการ-t mapfileตัวอย่างเช่นสิ่งนี้อ่านไปยังอาร์เรย์recordsและไม่เขียนอักขระบรรทัดใหม่ต่อท้ายไปยังองค์ประกอบอาร์เรย์:
mapfile -t records < filename
นอกจากนี้คุณยังสามารถใช้-tโดยไม่ต้องผ่านชื่ออาร์เรย์ นั่นคือมันใช้งานได้กับชื่ออาเรย์โดยนัยMAPFILEเช่นกัน
mapfileเปลือก builtin สนับสนุนตัวเลือกอื่น ๆ readarrayและมันสามารถผลัดกันจะเรียกว่าเป็น เรียกใช้help mapfile(และhelp readarray) เพื่อดูรายละเอียด
แต่คุณไม่ต้องการอ่านจากไฟล์คุณต้องการอ่านจากเอาต์พุตของคำสั่ง เพื่อให้บรรลุว่าใช้ทดแทนกระบวนการ คำสั่งนี้อ่านบรรทัดจากคำสั่งsome-commandด้วยarguments...เป็นอาร์กิวเมนต์บรรทัดคำสั่งและวางไว้ในmapfileอาเรย์เริ่มต้นMAPFILEของพวกเขาด้วยการยกเลิกตัวอักษรขึ้นบรรทัดใหม่:
mapfile -t < <(some-command arguments...)
การทดแทนกระบวนการแทนที่ด้วยชื่อไฟล์จริงที่สามารถอ่านเอาต์พุตของการทำงานได้ ไฟล์เป็นไพพ์ที่มีชื่อแทนที่จะเป็นไฟล์ปกติและใน Ubuntu มันจะมีชื่อว่า like (บางครั้งจะมีหมายเลขอื่นที่ไม่ใช่) แต่คุณไม่จำเป็นต้องกังวลเกี่ยวกับรายละเอียดเพราะเชลล์จะดูแลมัน เบื้องหลังทั้งหมด<(some-command arguments...)some-command arguments.../dev/fd/6363
คุณอาจคิดว่าคุณสามารถละเลยการเปลี่ยนตัวกระบวนการโดยใช้แทน แต่ที่จะไม่ทำงานเพราะเมื่อคุณมีท่อของคำสั่งหลาย ๆ แยกจากกันโดยทุบตีรันรันคำสั่งทั้งหมดในsubshells ดังนั้นทั้งสองและทำงานในสภาพแวดล้อมของตัวเองเริ่มต้นจาก แต่แยกจากสภาพแวดล้อมของเชลล์ที่คุณใช้ไปป์ไลน์ ใน subshell ที่ทำงานอาร์เรย์จะได้รับการเติมข้อมูล แต่จากนั้นอาร์เรย์นั้นจะถูกยกเลิกเมื่อคำสั่งสิ้นสุดลง ไม่เคยถูกสร้างหรือแก้ไขสำหรับผู้โทรsome-command arguments... | mapfile -t|some-command arguments...mapfile -tmapfile -tMAPFILEMAPFILE
นี่คือสิ่งที่ตัวอย่างในterdon คำตอบของรูปลักษณ์ที่ชอบในสิ่งทั้งปวงถ้าคุณใช้mapfile:
mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
echo "DIR: $d"
done
แค่นั้นแหละ. คุณไม่จำเป็นต้องตรวจสอบว่าIFSมีการตั้งค่าติดตามว่าไม่ได้มีการตั้งค่าไว้และมีค่าใดให้ตั้งเป็นบรรทัดใหม่จากนั้นรีเซ็ตหรือยกเลิกการตั้งค่าใหม่ในภายหลัง คุณไม่จำเป็นต้องปิดการใช้งานglobbing (ตัวอย่างเช่นด้วยset -f) - ซึ่งมีความจำเป็นจริงๆถ้าคุณจะใช้วิธีการอย่างจริงจังตั้งแต่ชื่อไฟล์อาจมี*, ?และ[--then เปิดใช้งานได้ ( set +f) หลังจากนั้น
คุณยังสามารถแทนที่ลูปนั้นด้วยprintfคำสั่งเดียวทั้งหมด- แม้ว่านี่จะไม่เป็นประโยชน์จริง ๆmapfileเพราะคุณสามารถทำได้ไม่ว่าคุณจะใช้mapfileหรือวิธีอื่นในการเติมอาเรย์ นี่เป็นรุ่นที่สั้นกว่า:
mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"
สิ่งสำคัญคือให้จำไว้ว่าterdon กล่าวไว้ว่าการใช้งานทีละบรรทัดนั้นไม่เหมาะสมเสมอไปและจะทำงานไม่ถูกต้องกับชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ ฉันแนะนำให้ตั้งชื่อไฟล์ด้วยวิธีนี้ แต่มันอาจเกิดขึ้นได้รวมถึงโดยบังเอิญ
ไม่มีวิธีแก้ปัญหาที่เหมาะกับทุกขนาดจริงๆ
คุณขอให้ "คำสั่งทั่วไปสำหรับทุกสถานการณ์" และวิธีการใช้mapfileวิธีการที่ค่อนข้างเป็นเป้าหมาย แต่ฉันขอให้คุณพิจารณาความต้องการของคุณอีกครั้ง
งานที่แสดงด้านบนสามารถทำได้ดีกว่าด้วยfindคำสั่งเดียว:
find . -type d -printf 'DIR: %p\n'
คุณสามารถใช้คำสั่งภายนอกเช่นsedเพิ่มDIR: ไปยังจุดเริ่มต้นของแต่ละบรรทัด นี่เป็นเนื้อหาที่ค่อนข้างน่าเกลียดและไม่เหมือนกับคำสั่ง find ที่จะเพิ่มคำนำหน้า "พิเศษ" ในชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ แต่มันทำงานโดยไม่คำนึงถึงอินพุตดังนั้นมันจึงเป็นไปตามความต้องการของคุณและมันก็ยังดีกว่า ตัวแปรหรืออาร์เรย์ :
find . -type d | sed 's/^/DIR: /'
หากคุณต้องการแสดงรายการและดำเนินการกับแต่ละไดเรกทอรีที่พบเช่นการเรียกใช้some-commandและผ่านเส้นทางไดเรกทอรีเป็นอาร์กิวเมนต์findให้คุณทำเช่นนั้น:
find . -type d -print -exec some-command {} \;
เป็นอีกตัวอย่างลองกลับไปที่งานทั่วไปของการเพิ่มคำนำหน้าให้แต่ละบรรทัด สมมติว่าฉันต้องการที่จะเห็นผลลัพธ์ของhelp mapfileแต่จำนวนบรรทัด ฉันจะไม่ใช้mapfileสิ่งนี้หรือวิธีอื่นใดที่อ่านมันลงในตัวแปรเชลล์หรืออาเรย์เชลล์ หากhelp mapfile | cat -nว่าไม่ได้จัดรูปแบบที่ฉันต้องการฉันสามารถใช้awk:
help mapfile | awk '{ printf "%3d: %s\n", NR, $0 }'
การอ่านเอาต์พุตทั้งหมดของคำสั่งในตัวแปรหรืออาเรย์เดี่ยวบางครั้งก็มีประโยชน์และเหมาะสม แต่ก็มีข้อเสียที่สำคัญ คุณไม่เพียงต้องจัดการกับความซับซ้อนเพิ่มเติมของการใช้เชลล์ของคุณเมื่อคำสั่งที่มีอยู่หรือการรวมกันของคำสั่งที่มีอยู่อาจทำสิ่งที่คุณต้องการเช่นกันหรือดีกว่า แต่ผลลัพธ์ทั้งหมดของคำสั่งจะต้องเก็บไว้ในหน่วยความจำ บางครั้งคุณอาจรู้ว่าไม่ใช่ปัญหา แต่บางครั้งคุณอาจกำลังประมวลผลไฟล์ขนาดใหญ่
ทางเลือกที่ใช้กันทั่วไปและบางครั้งทำถูกต้องคือการอ่านอินพุตบรรทัดต่อบรรทัดด้วยread -rการวนซ้ำ mapfileถ้าคุณไม่จำเป็นต้องเก็บสายก่อนหน้านี้ในขณะที่ปฏิบัติการในบรรทัดต่อมาและคุณจะต้องใช้การป้อนข้อมูลนานแล้วมันอาจจะดีกว่า แต่มันเกินไปควรหลีกเลี่ยงในกรณีเมื่อคุณสามารถเพียงแค่ท่อกับคำสั่งที่สามารถทำผลงานซึ่งเป็นกรณีส่วนใหญ่