วนซ้ำซ้ำในไฟล์ต่างๆในไดเรกทอรี


15

วนซ้ำซ้ำในไฟล์ต่างๆในไดเรกทอรีสามารถทำได้อย่างง่ายดายโดย:

find . -type f -exec bar {} \;

อย่างไรก็ตามข้างต้นไม่สามารถใช้ได้กับสิ่งที่ซับซ้อนมากขึ้นซึ่งต้องมีกิ่งที่มีเงื่อนไขการวนซ้ำ ฯลฯ ต้องทำ ฉันเคยใช้สิ่งนี้สำหรับข้างต้น:

while read line; do [...]; done < <(find . -type f)

อย่างไรก็ตามดูเหมือนว่าสิ่งนี้จะใช้ไม่ได้กับไฟล์ที่มีอักขระที่ไม่ชัดเจน

$ touch $'a\nb'
$ find . -type f
./a?b

มีทางเลือกอื่นที่จัดการกับตัวละครที่คลุมเครือเช่นนี้หรือไม่


1
find ... -exec bash -c 'echo filename is in \$0: "$0"' {} \;เป็นวิธีที่ดีกว่าที่จะทำ
jw013

คุณสามารถแก้ปัญหานี้และให้การออกแบบเดิมของคุณโดยการเปลี่ยนไปread line IFS= read -r lineตัวละครเท่านั้นที่จะทำลายมันก็ขึ้นบรรทัดใหม่
แพทริค

1
@Patrick แต่ชื่อไฟล์สามารถมีการขึ้นบรรทัดใหม่ได้ นั่นเป็นเหตุผลที่-d $'\0'ดีกว่า
godlygeek

คำตอบ:


7

ยังใช้เพื่อความปลอดภัยอื่นfind :

while IFS= read -r -d '' -u 9
do
    [Do something with "$REPLY"]
done 9< <( find . -type f -exec printf '%s\0' {} + )

(ใช้งานได้กับ POSIX ใด ๆfindแต่ส่วนของเชลล์ต้องการทุบตีด้วย * BSD และ GNU find คุณสามารถใช้-print0แทน-exec printf '%s\0' {} +มันจะเร็วขึ้นเล็กน้อย)

ทำให้สามารถใช้อินพุตมาตรฐานภายในลูปและทำงานกับพา ธใดก็ได้


1
เพราะฉันต้องค้นหามัน: "อ่าน ... ถ้าไม่มีชื่อใด ๆ การอ่านบรรทัดจะถูกกำหนดให้กับตัวแปร REPLY" ดังนั้นdo echo "Filename is '$REPLY'"
แอนดรูว์

9

การทำเช่นนี้ง่ายเหมือน:

find -exec sh -c 'inline script "$0"' {} \;

หรือ...

find -exec executable_script {} \;

5

วิธีที่ง่ายที่สุด (ยังปลอดภัย) คือการใช้เปลือกกลม:

$ for f in *; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
h:

ในการทำให้ค่าตอบแทนด้านบนเป็นไดเรกทอรีย่อย (เป็น bash) คุณสามารถใช้globstarตัวเลือก ตั้งค่าdotglobให้จับคู่ไฟล์ที่ชื่อขึ้นต้นด้วย.:

$ shopt -s globstar dotglob
$ for f in **/*; do printf ":%s:\n" "$f"; done 
:a b:
:c
d:
:-e:
:e  f:
:foo:
:foo/file1:
:foo/file two:
h:

ระวังให้มากถึงทุบตี 4.2, **/เชื่อมโยงซ้ำไปยังลิงก์สัญลักษณ์ไปยังไดเรกทอรี ตั้งแต่ทุบตี 4.3, **/recurses ลงในไดเรกทอรีเช่นเดียวกับfindเท่านั้น

วิธีแก้ไขปัญหาทั่วไปอีกประการหนึ่งคือใช้find -print0กับxargs -0:

$ touch -- 'a b' $'c\nd' $'e\tf' $'g\rh' '-e'
$ find . -type f -print0 | xargs -0 -I{} printf ":%s:\n" {}
h:/g
:./e    f:
:./a b:
:./-e:
:./c
d:

โปรดทราบว่าเป็นจริงที่ถูกต้องตั้งแต่ชื่อไฟล์ที่มีh:/g\r


4

เป็นการยากที่จะทำการวนลูปการอ่านของคุณแบบพกพาได้ แต่สำหรับ bash โดยเฉพาะคุณสามารถลองสิ่งนี้ได้

ส่วนที่เกี่ยวข้อง:

while IFS= read -d $'\0' -r file ; do
        printf 'File found: %s\n' "$file"
done < <(find . -iname 'foo*' -print0)

ที่สั่งfindให้พิมพ์เอาต์พุตที่คั่นด้วยอักขระ NUL (0x00) และreadดึงข้อมูลบรรทัดที่คั่นด้วย NUL (-d $'\0' ) โดยไม่ต้องจัดการแบ็กสแลชเป็น escapes สำหรับอักขระอื่น ( -r) และไม่ทำการแยกคำใด ๆ ในบรรทัด ( IFS=) เนื่องจาก 0x00 เป็นไบต์ที่ไม่สามารถเกิดขึ้นได้ในชื่อไฟล์หรือเส้นทางใน Unix สิ่งนี้ควรจัดการกับปัญหาชื่อไฟล์แปลก ๆ ทั้งหมดของคุณ


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