จำกัด POSIX ค้นหาความลึกที่เฉพาะเจาะจงหรือไม่


15

ฉันสังเกตเห็นเมื่อเร็ว ๆ นี้ว่าข้อกำหนด POSIX สำหรับfindไม่รวมคุณสมบัติ-maxdepthหลัก

สำหรับผู้ที่ไม่คุ้นเคยวัตถุประสงค์-maxdepthหลักก็เพื่อ จำกัด จำนวนระดับที่ลึกfindลงไป -maxdepth 0ส่งผลให้อาร์กิวเมนต์บรรทัดรับคำสั่งเท่านั้นที่ถูกประมวลผล -maxdepth 1จะจัดการกับผลลัพธ์โดยตรงภายในอาร์กิวเมนต์บรรทัดคำสั่ง ฯลฯ

ฉันจะได้รับพฤติกรรมเทียบเท่ากับ non-POSIX -maxdepthprimary โดยใช้เพียงตัวเลือกและเครื่องมือที่ระบุโดย POSIX ได้อย่างไร

(หมายเหตุ: แน่นอนฉันสามารถเทียบเท่า-maxdepth 0โดยใช้-pruneเป็นตัวถูกดำเนินการแรก แต่ไม่ได้ขยายไปถึงความลึกอื่น ๆ )


@StevenPenny, FreeBSD's -depth -2, -depth 1... สามารถมองเห็นวิธีการได้ดีกว่า GNU's -maxdepth/-mindepth
Stéphane Chazelas

@ StéphaneChazelasทางใดทางหนึ่ง - POSIX find ควรมีอย่างใดอย่างหนึ่ง; มิฉะนั้นมันจะพิการ
สตีเวนเพนนี

1
อย่างน้อยสำหรับ-maxdepth/ -mindepthมีทางเลือกที่สมเหตุสมผล (โปรดทราบว่า-pathเป็นส่วนเสริมล่าสุดของ POSIX) ทางเลือกสำหรับ-timexyหรือ-mtime -3m(หรือ-mmin -3) มีความยุ่งยากมากขึ้น บางคนชอบ-execdir/ -deleteไม่มีทางเลือกที่เชื่อถือได้
Stéphane Chazelas

2
@StevenPenny โปรดเข้าสู่ตั๋วที่austingroupbugs.netเพื่อขอเพิ่ม ฉันเห็นสิ่งต่าง ๆ ถูกเพิ่มเข้ามาโดยไม่จำเป็นต้องมีสปอนเซอร์เมื่อมีเหตุผลที่ชัดเจน การกระทำที่ดีกว่าน่าจะเป็นการเพิ่มการใช้งานให้มากขึ้นก่อนดังนั้น POSIX จะต้องระบุที่มีอยู่ซึ่งโดยทั่วไปมักไม่ค่อยมีเนื้อหา
Stéphane Chazelas

@ StéphaneChazelasในกรณีของฉันฉันเพียงแค่ตั้งชื่อไฟล์โดยตรง แต่ขอบคุณ; ฉันอาจยื่นตั๋วหากสิ่งนี้เกิดขึ้นอีกครั้ง
Steven Penny

คำตอบ:


7

คุณสามารถใช้-pathเพื่อจับคู่ความลึกที่กำหนดและตัดที่นั่น เช่น

find . -path '*/*/*' -prune -o -type d -print

จะเป็น maxdepth 1 ตามที่*ตรงกับ., การ*/*แข่งขัน./dir1และการ*/*/*แข่งขัน./dir1/dir2ที่มีการตัด หากคุณใช้ไดเรคทอรี่เริ่มต้นที่สมบูรณ์คุณจะต้องเพิ่มส่วนนำ/ไปยังส่วน-pathด้วย


อืมมมยุ่งยาก คุณไม่สามารถลบหนึ่งเลเยอร์ของ/*ออกจากส่วนท้ายของรูปแบบนำตัว-oดำเนินการออกมาและได้ผลลัพธ์เดียวกันหรือไม่
Wildcard

ไม่เพราะเข้า*คู่/กันดังนั้น dir a/b/c/d/eจะพอดี-path */*เศร้า
meuh

แต่a/b/c/d/eจะไม่สามารถเข้าถึงได้เพราะ-pruneจะถูกนำไปใช้กับa/b....
Wildcard

1
ขออภัยฉันอ่านผิด-pruneและ-oถูกลบไปแล้ว หากคุณให้-pruneปัญหาคือว่า*/*จะไม่ตรงกับสิ่งที่อยู่ในระดับดังกล่าวข้างต้น maxdepth aเป็นเช่นไดเรกทอรีเดียว
meuh

11

วิธีการของ @ meuh ไม่มีประสิทธิภาพเนื่องจาก-maxdepth 1วิธีการของเขายังช่วยให้findอ่านเนื้อหาของไดเรกทอรีที่ระดับ 1 เพื่อเพิกเฉยต่อสิ่งเหล่านี้ในภายหลัง มันจะไม่ทำงานอย่างถูกต้องกับfindการใช้งานบางอย่าง(รวมถึง GNU find) ถ้าบางชื่อไดเรกทอรีมีลำดับของไบต์ที่ไม่ได้สร้างตัวละครที่ถูกต้องในสถานที่ของผู้ใช้ (เช่นชื่อไฟล์ในการเข้ารหัสอักขระที่แตกต่างกัน)

find . \( -name . -o -prune \) -extra-conditions-and-actions

เป็นวิธีที่ยอมรับได้มากขึ้นในการใช้ GNU -maxdepth 1(หรือ FreeBSD's -depth -2)

โดยทั่วไปแล้ว-depth 1คุณต้องการ ( -mindepth 1 -maxdepth 1) ตามที่คุณไม่ต้องการพิจารณา.(ความลึก 0) จากนั้นง่ายยิ่งขึ้น:

find . ! -name . -prune -extra-conditions-and-actions

สำหรับ-maxdepth 2ที่จะกลายเป็น:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

และนั่นคือสิ่งที่คุณทำงานในปัญหาของตัวละครที่ไม่ถูกต้อง

ตัวอย่างเช่นหากคุณมีไดเรกทอรีที่เรียกว่าStéphaneแต่éมีการเข้ารหัสในชุดอักขระ iso8859-1 (aka latin1) (0xe9 ไบต์) ซึ่งพบได้ทั่วไปในยุโรปตะวันตกและอเมริกาจนถึงกลางปี ​​2000 ดังนั้น 0xe9 ไบต์จะไม่เป็น อักขระที่ถูกต้องใน UTF-8 ดังนั้นในโลแคล UTF-8 *อักขระตัวแทน (ที่มีfindการใช้งานบางอย่าง) จะไม่ตรงกับStéphaneที่*เป็น 0 หรือมากกว่าตัวอักษรและ 0xe9 ไม่ใช่ตัวอักษร

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

ของฉันfind(เมื่อผลลัพธ์ไปยังเทอร์มินัล) แสดง 0xe9 ไบต์ที่ไม่ถูกต้องดังกล่าว?ข้างต้น คุณจะเห็นว่าSt<0xe9>phane/Chazelasไม่ใช่pruned

คุณสามารถหลีกเลี่ยงได้โดยทำ:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

แต่โปรดทราบว่าจะมีผลกับการตั้งค่าภาษาทั้งหมดfindและแอปพลิเคชันใด ๆ ที่ทำงาน (เช่นผ่านภาค-execแสดง)

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

ตอนนี้ฉันได้รับจริง ๆ-maxdepth 2แต่ทราบว่าéในStéphaneที่สองถูกเข้ารหัสอย่างถูกต้องใน UTF-8 แสดงเป็น??อย่างไร 0xc3 0xa9 ไบต์ (ถือว่าเป็นอักขระสองตัวที่ไม่ได้กำหนดใน C ตำแหน่งที่ตั้ง) ของการเข้ารหัส UTF-8 ของé ไม่ใช่อักขระที่พิมพ์ได้ใน C locale

และถ้าฉันเพิ่ม a ลงไป-name '????????'ฉันจะได้ค่าStéphaneผิด (อันที่เข้ารหัสใน iso8859-1)

หากต้องการนำไปใช้กับเส้นทางที่กำหนดเองแทนคุณ.จะต้อง:

find some/dir/. ! -name . -prune ...

สำหรับ-mindepth 1 -maxdepth 1หรือ:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

-maxdepth 2สำหรับ

ฉันจะยังคงทำ:

(cd -P -- "$dir" && find . ...)

ก่อนเพราะนั่นทำให้เส้นทางสั้นลงซึ่งทำให้มีโอกาสน้อยที่จะวิ่งเข้าไปในเส้นทางยาวเกินไปหรือหาเรื่องรายการปัญหาที่ยาวเกินไปแต่ยังต้องหลีกเลี่ยงข้อเท็จจริงที่findไม่สามารถรองรับอาร์กิวเมนต์เส้นทางโดยพลการ (ยกเว้น-fกับ FreeBSD find) เนื่องจากจะทำให้หายใจไม่ออก ค่า$dirชอบ!หรือ-print...


-oรวมกันด้วยการปฏิเสธเป็นเคล็ดลับเรื่องธรรมดาที่จะเรียกใช้สองชุดเป็นอิสระจาก-condition/ ใน-actionfind

หากคุณต้องการทำงาน-action1กับการประชุมไฟล์-condition1และเป็นอิสระ-action2ในการประชุมไฟล์-condition2คุณไม่สามารถทำได้:

find . -condition1 -action1 -condition2 -action2

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

มิได้:

find . -contition1 -action1 -o -condition2 -action2

ในฐานะที่-action2จะไม่ถูกเรียกใช้ไฟล์ที่ตอบสนองทั้งเงื่อนไข

find . \( ! -condition1 -o -action1 \) -condition2 -action2

ทำงานตามที่\( ! -condition1 -o -action1 \)ต้องการจะแก้ไขเป็นจริงสำหรับทุกไฟล์ ที่ถือว่า-action1เป็นกระทำ (เช่น-prune, -exec ... {} +) ที่เสมอกลับจริง สำหรับการดำเนินการเช่น-exec ... \;นั้นอาจส่งคืนค่าเท็จคุณอาจต้องการเพิ่มรายการอื่น-o -somethingที่-somethingไม่เป็นอันตราย แต่ส่งกลับค่าจริงเช่น-trueใน GNU findหรือ-links +0หรือ -name '*'(ถึงแม้ว่าจะบันทึกปัญหาเกี่ยวกับอักขระที่ไม่ถูกต้องด้านบน)


1
สักวันฉันจะเจอไฟล์ภาษาจีนเป็นกลุ่มและฉันดีใจมากที่ฉันได้อ่านคำตอบมากมายเกี่ยวกับสถานที่และตัวละครที่ถูกต้อง :)
สัญลักษณ์แทน

2
@ Wildcard คุณ (และยิ่งกว่านั้นคือคนจีน) มีแนวโน้มที่จะประสบปัญหากับชื่อไฟล์ภาษาอังกฤษ, ภาษาฝรั่งเศส ... กว่าชื่อไฟล์ภาษาจีนเนื่องจากชื่อไฟล์ภาษาจีนมักเข้ารหัสใน UTF-8 มากกว่าชื่อไฟล์ของสคริปต์ตัวอักษร ที่สามารถครอบคลุมโดย charset ไบต์เดียวซึ่งเป็นบรรทัดฐานจนถึงจนกระทั่งค่อนข้างเร็ว มีตัวอักษรแบบหลายไบต์อื่น ๆ เพื่อให้ครอบคลุมตัวอักษรจีน แต่ฉันคาดว่าคนจีนจะเปลี่ยนมาใช้ UTF-8 เร็วกว่าชาวตะวันตกเนื่องจากชุดอักขระเหล่านั้นมีปัญหาที่น่ารังเกียจมากมาย ดูตัวอย่างการแก้ไขด้วย
Stéphane Chazelas

0

ฉันพบปัญหาที่ฉันต้องการวิธีจำกัดความลึกเมื่อค้นหาหลายเส้นทาง (แทนที่จะเป็นเพียง.)

ตัวอย่างเช่น:

$ find dir1 dir2 -name myfile -maxdepth 1

นี่ทำให้ฉันมีวิธีอื่นในการใช้ -regex ส่วนสำคัญคือ:

-regex '(<list of paths | delimited>)/<filename>'

ดังนั้นข้างต้นจะเป็น:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

ไม่มีชื่อไฟล์:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

ในที่สุดสำหรับ-maxdepth 2regex เปลี่ยนเป็น:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'


1
คำถามนี้ถามถึงวิธีแก้ปัญหามาตรฐาน (เหมือนใน POSIX) นอกจากนี้ยัง-maxdepthจะทำงานร่วมกับหลายเส้นทางการค้นหา
Kusalananda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.