อัปเดต 2020 สำหรับผู้ใช้ Linux:
หากคุณมีรุ่นขึ้นไปวันที่ทุบตี (4.4-alpha หรือดีกว่า) ในขณะที่คุณอาจจะทำอย่างไรถ้าคุณอยู่ในลินุกซ์แล้วคุณควรจะใช้คำตอบของเบนจามินดับบลิวของ
หากคุณใช้ Mac OS ซึ่ง - ฉันตรวจสอบครั้งล่าสุด - ยังคงใช้ bash 3.2 หรือใช้ bash รุ่นเก่าอยู่ให้ไปยังส่วนถัดไป
คำตอบสำหรับ bash 4.3 หรือก่อนหน้า
นี่คือทางออกหนึ่งในการรับเอาต์พุตfind
ลงในbash
อาร์เรย์:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
นี่เป็นเรื่องยุ่งยากเนื่องจากโดยทั่วไปชื่อไฟล์อาจมีช่องว่างบรรทัดใหม่และอักขระที่เป็นศัตรูกับสคริปต์อื่น ๆ วิธีเดียวที่จะใช้find
และแยกชื่อไฟล์ออกจากกันอย่างปลอดภัยคือการใช้-print0
ซึ่งพิมพ์ชื่อไฟล์ที่คั่นด้วยอักขระ null สิ่งนี้จะไม่สะดวกมากนักหาก bash readarray
/ mapfile
ฟังก์ชั่นรองรับสตริงที่คั่นด้วย null แต่ไม่เป็นเช่นนั้น Bash read
ทำและนั่นนำเราไปสู่ลูปด้านบน
[คำตอบนี้เขียนขึ้นครั้งแรกในปี 2014 หากคุณมี bash เวอร์ชันล่าสุดโปรดดูการอัปเดตด้านล่าง]
มันทำงานอย่างไร
บรรทัดแรกสร้างอาร์เรย์ว่าง: array=()
ทุกครั้งที่ดำเนินการread
คำสั่งชื่อไฟล์ที่คั่นด้วย null จะถูกอ่านจากอินพุตมาตรฐาน -r
ตัวเลือกที่จะบอกread
ให้ออกไปจากตัวอักษรทับขวาเพียงอย่างเดียว -d $'\0'
บอกread
ว่าการป้อนข้อมูลจะเป็นโมฆะคั่น เนื่องจากเราละเว้นชื่อเปลือกทำให้การป้อนข้อมูลลงในชื่อเริ่มต้น:read
REPLY
งบผนวกชื่อไฟล์ใหม่อาร์เรย์array+=("$REPLY")
array
บรรทัดสุดท้ายรวมการเปลี่ยนเส้นทางและการแทนที่คำสั่งเพื่อจัดเตรียมเอาต์พุตfind
ไปยังอินพุตมาตรฐานของwhile
ลูป
เหตุใดจึงต้องใช้การทดแทนกระบวนการ
หากเราไม่ได้ใช้การทดแทนกระบวนการลูปสามารถเขียนเป็น:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
ในด้านบนผลลัพธ์ของfind
จะถูกเก็บไว้ในไฟล์ชั่วคราวและไฟล์นั้นถูกใช้เป็นอินพุตมาตรฐานของลูป while แนวคิดในการทดแทนกระบวนการคือการทำให้ไฟล์ชั่วคราวดังกล่าวไม่จำเป็น ดังนั้นแทนที่จะให้while
ลูปได้รับ stdin จากtmpfile
เราสามารถให้มันรับ stdin <(find . -name ${input} -print0)
ได้
การทดแทนกระบวนการมีประโยชน์อย่างกว้างขวาง ในหลาย ๆ ที่ที่คำสั่งต้องการอ่านจากไฟล์คุณสามารถระบุการทดแทนกระบวนการแทน<(...)
ชื่อไฟล์ มีรูปแบบที่คล้ายคลึงกัน>(...)
ซึ่งสามารถใช้แทนชื่อไฟล์ที่คำสั่งต้องการเขียนไปยังไฟล์
เช่นเดียวกับอาร์เรย์การทดแทนกระบวนการเป็นคุณสมบัติของ bash และเชลล์ขั้นสูงอื่น ๆ ไม่ใช่ส่วนหนึ่งของมาตรฐาน POSIX
ทางเลือก: lastpipe
หากต้องการlastpipe
สามารถใช้แทนการทดแทนกระบวนการ (ปลายหมวก: ซีซาร์ ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
บอกให้ bash รันคำสั่งสุดท้ายในไปป์ไลน์ในเชลล์ปัจจุบัน (ไม่ใช่พื้นหลัง) ด้วยวิธีนี้สิ่งที่array
เหลืออยู่หลังจากท่อส่งเสร็จสมบูรณ์ เพราะจะมีผลหากการควบคุมงานถูกปิดเราทำงานlastpipe
set +m
(ในสคริปต์ซึ่งตรงข้ามกับบรรทัดคำสั่งการควบคุมงานจะปิดโดยค่าเริ่มต้น)
หมายเหตุเพิ่มเติม
คำสั่งต่อไปนี้สร้างตัวแปรเชลล์ไม่ใช่เชลล์อาร์เรย์:
array=`find . -name "${input}"`
หากคุณต้องการสร้างอาร์เรย์คุณจะต้องใส่ parens รอบ ๆ ผลลัพธ์ของ find อย่างไร้เดียงสาเราสามารถ:
array=(`find . -name "${input}"`)
ปัญหาคือเชลล์ทำการแยกคำกับผลลัพธ์find
เพื่อไม่ให้องค์ประกอบของอาร์เรย์เป็นสิ่งที่คุณต้องการ
อัปเดต 2019
ตั้งแต่เวอร์ชัน 4.4-alpha ตอนนี้ bash สนับสนุน-d
ตัวเลือกเพื่อให้ไม่จำเป็นต้องใช้ลูปด้านบนอีกต่อไป สามารถใช้:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้โปรดดู (และ upvote) คำตอบของเบนจามินดับบลิวของ