วิธีการส่งผ่านผลลัพธ์ของ "find" เป็นรายการไฟล์?


11

สถานการณ์คือฉันมีเครื่องเล่น MP3 mpg321ที่ยอมรับรายการของไฟล์เป็นข้อโต้แย้ง ฉันเก็บเพลงของฉันไว้ในไดเรกทอรีที่ชื่อ "เพลง" ซึ่งมีอีกไม่กี่ไดเรกทอรี ฉันแค่ต้องการเล่นทั้งหมดดังนั้นฉันจึงรันโปรแกรมด้วย

mpg321 $(find /music -iname "*\.mp3")

. ปัญหาคือชื่อไฟล์บางชื่อมีช่องว่างในนั้นและโปรแกรมแบ่งชื่อเหล่านั้นออกเป็นส่วนเล็ก ๆ และบ่นเกี่ยวกับไฟล์ที่หายไป การตัดผลลัพธ์ของfindคำพูดใน

mpg321 "$(find /music -iname "*\.mp3")"

ไม่ช่วยเพราะจะกลายเป็น "ชื่อไฟล์" ขนาดใหญ่ซึ่งไม่พบ

ฉันจะทำสิ่งนี้ได้อย่างไร หากเป็นเช่นนั้นฉันกำลังใช้bashงาน แต่จะเปลี่ยนเป็นzshเร็ว ๆ นี้

คำตอบ:


17

ลองใช้การค้นหา-print0หรือ-printfตัวเลือกร่วมกับxargsสิ่งนี้:

find /music -iname "*\.mp3" -print0 | xargs -0 mpg321

วิธีการทำงานของหน้านี้ถูกอธิบายโดยหน้าคู่มือของ find :

-print0

ทรู; พิมพ์ชื่อไฟล์แบบเต็มบนเอาต์พุตมาตรฐานตามด้วยอักขระ null (แทนอักขระขึ้นบรรทัดใหม่ที่ -print ใช้) สิ่งนี้อนุญาตให้ชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่หรือพื้นที่สีขาวประเภทอื่น ๆ สามารถตีความได้อย่างถูกต้องโดยโปรแกรมที่ประมวลผลเอาต์พุตการค้นหา ตัวเลือกนี้สอดคล้องกับตัวเลือก -0 ของ xargs


ขออภัยสำหรับการแก้ไขทั้งหมด ฉันดูเหมือนจะจำรูปแบบ -print0 xarg -0 ล้มเหลวในช่องว่างประเภทอื่น แต่ฉันไม่พบตัวอย่าง
Steven D

โอ้ใช่แล้วมันใช้ได้! แต่ฉันก็ยังสงสัยว่าทำไมmpg321 $(find /music -iname "*\.mp3" -print0)ไม่
phunehehe

1
ฉันเชื่อว่ามันใช้งานไม่ได้ด้วยเหตุผลสองประการ: (1) ชื่อไฟล์ถูกคั่นด้วยอักขระ null ซึ่งไม่ใช่สิ่งที่ mpg321 คาดหวังไว้และ (2) xargs กำลังทำงานเพื่อหลบหนีช่องว่างอื่น ๆ ไม่ใช่ หา.
Steven D

2
@phunehehe, @Steven: mpg321ไม่มีอะไรเกี่ยวข้องกับมันมันคือเชลล์ที่findแยกเอาท์พุทของการแยกอาร์กิวเมนต์ออก และ-print0 | xargs -0จะทำงานกับทุกชื่อไฟล์ที่เป็นไปได้
Gilles 'หยุดความชั่วร้าย'

8
find /music -iname "*\.mp3" -exec mpg123 {} +

ด้วย GNU พบว่าคุณยังสามารถใช้-print0และxargs -0แต่มีจุดเล็ก ๆ ในการเรียนรู้ยังเป็นเครื่องมืออื่น -exec ... {} +ไวยากรณ์ได้รับการกล่าวถึงเล็กน้อยเพราะลินุกซ์ที่ได้มามันช้ากว่า-print0แต่มีเหตุผลที่จะไม่ใช้มันตอนนี้ไม่มี

ด้วย zsh หรือ bash 4 สิ่งนี้ง่ายกว่ามาก:

mpg123 **/*.[Mm][Pp]3

ใน zsh เท่านั้นคุณสามารถสร้าง case-insensitive (ส่วนหนึ่งของ) pattern:

mpg123 (#i)**/*.mp3

+1 ถึงสิ่งนี้ "find -print0" เป็นแฮ็กที่น่าเกลียด
p-static

2

ฉันคิดว่าทางออกของ Stevenดีที่สุด แต่อีกวิธีคือใช้-Iค่าสถานะxargs ซึ่งช่วยให้คุณระบุสตริงที่จะถูกแทนที่ในคำสั่งด้วยอาร์กิวเมนต์ (แทนที่จะเพียงต่อท้ายอาร์กิวเมนต์ลงท้ายคำสั่ง) คุณสามารถใช้สิ่งนั้นเพื่ออ้างถึงข้อโต้แย้ง:

find /music -iname "*\.mp3" | xargs -0 -Ifoo mpg321 "foo"

ฉันไม่เข้าใจคำตอบนี้ ... คุณกำลังใช้ xargs ของธงโดยไม่ต้องพบของ-0 -print0นอกจากนี้-Iค่าสถานะของ xargs มีจำนวนของผลที่ตามมา ซึ่งแตกต่างจากเพียงธรรมดาxargsซึ่งจะบีบอัดทุกบรรทัดจาก stdin เป็นคำสั่งเดียวxargs -Iจะดำเนินการที่mpg321อาจเกิดขึ้นหลายร้อยหรือหลายพันครั้ง (หนึ่งครั้งสำหรับแต่ละไฟล์) ซึ่งไม่ได้ตั้งใจอย่างแน่นอน นอกจากนี้โปรดจำไว้ว่าการอ้างฟูไม่จำเป็นเช่นเดียวกับxargsสิ่งนี้ภายใน โปรดสังเกตว่าคุณบีบอัดชื่อไฟล์ทั้งหมดบนบรรทัดบรรทัดและส่งไปที่xargs -Iพวกเขาจะไม่เปิด
หก

1

โดยปกติแล้วจะเป็นการดีที่สุดโดยตรง-exec ${tgt_process} \{\} +แต่ถ้าคุณต้องการรับรายการชื่อไฟล์ที่คั่นด้วยความน่าเชื่อถือในไฟล์หรือสตรีมfindด้วยเหตุผลใดก็ตามคุณสามารถทำสิ่งนี้ได้:

find -exec sh -c 'printf "///%s///\n" "$@"' -- \{\} +

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

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

แต่อีกครั้งสิ่งนี้จำเป็นเฉพาะถ้าคุณไม่สามารถบอกเหตุผล-execของคุณไม่ตรง$tgt_processตามที่ควรเป็นเป้าหมายของคุณ สำหรับสิ่งหนึ่งวิธีการข้างต้นยังคงต้องแยกวิเคราะห์ ตัวอย่างเช่นหากคุณต้องการให้แต่ละเชลล์ชื่อไฟล์อ้างถึงก่อนอื่นคุณต้องตรวจสอบให้แน่ใจว่าได้ใส่เครื่องหมายคำพูดอย่างหนักในชื่อไฟล์แล้ว:

find ... + | sed 's|'\''|&"&"&|g;s|///|'\''|g'

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


0

อีกวิธีในการทำเช่นนี้คือการหลีกเลี่ยงอักขระพิเศษทั้งหมดที่มาในชื่อไฟล์ของคุณ เช่น:

find /music -iname "*\.mp3" | sed 's!\([] \*\$\/&[]\)!\\\1!g' | xargs mpg321

โดยทั่วไปจะส่งชื่อไฟล์ที่มีการ Escape อย่างเหมาะสมไปยัง xargs สำหรับการดำเนินการและจะไม่มีปัญหาใด ๆ


1
สิ่งนี้จะยังคงขึ้นบรรทัดใหม่ในชื่อไฟล์ และคุณก็ทำได้เช่นกันsed 's|.|\\&|g'- นั่นคือสิ่งที่ POSIX แนะนำต่อไป
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.