การค้นหาไฟล์ทั้งหมดที่มีนามสกุลที่กำหนดซึ่งมีชื่อฐานคือชื่อของไดเรกทอรีหลัก


9

ฉันต้องการค้นหา*.pdfไฟล์ทุกไฟล์ซ้ำในไดเรกทอรี~/fooที่มีชื่อฐานตรงกับชื่อของไดเรกทอรีหลักของไฟล์

ตัวอย่างเช่นสมมติว่าโครงสร้างไดเรกทอรี~/fooมีลักษณะเช่นนี้

foo
├── dir1
│   ├── dir1.pdf
│   └── dir1.txt
├── dir2
│   ├── dir2.tex
│   └── spam
│       └── spam.pdf
└── dir3
    ├── dir3.pdf
    └── eggs
        └── eggs.pdf

การรันคำสั่งที่ฉันต้องการจะกลับมา

~/foo/dir1/dir1.pdf
~/foo/dir2/spam/spam.pdf
~/foo/dir3/dir3.pdf
~/foo/dir3/eggs/eggs.pdf

เป็นไปได้โดยใช้findหรือยูทิลิตี้คอร์อื่น ๆ ? ฉันถือว่าสิ่งนี้ทำได้โดยใช้-regexตัวเลือกfindแต่ฉันไม่แน่ใจว่าจะเขียนรูปแบบที่ถูกต้องได้อย่างไร


ใช่ฉันจะแกล้งทำเป็นตัวอย่างตอนนี้
Brian Fitzpatrick

1
@Inian เพิ่มตัวอย่าง สิ่งนี้ช่วยได้ไหม?
Brian Fitzpatrick

คำตอบ:


16

ด้วย GNU find:

find . -regextype egrep -regex '.*/([^/]+)/\1\.pdf'
  • -regextype egrep ใช้ regrep สไตล์ egrep
  • .*/ จับคู่คำสั่งผู้ปกครองที่ยิ่งใหญ่
  • ([^/]+)/ จับคู่ dir หลักในกลุ่ม
  • \1\.pdfใช้backreferenceเพื่อจับคู่ชื่อไฟล์เป็นพาเรนต์ dir

ปรับปรุง

หนึ่ง (ตัวเองสำหรับหนึ่ง) อาจคิดว่า.*มันโลภพอมันไม่จำเป็นที่จะแยก/ออกจากการจับคู่ผู้ปกครอง:

find . -regextype egrep -regex '.*/(.+)/\1\.pdf'

คำสั่งด้านบนใช้งานไม่ได้เพราะมันใช้การได้ดี./a/b/a/b.pdf:

  • .*/ ไม้ขีด ./
  • (.+)/ ไม้ขีด a/b/
  • \1.pdf ไม้ขีด a/b.pdf

เด็ดมาก หวังว่าฉันจะสามารถ regex นี้ได้ดี
Brian Fitzpatrick

หรือfind . -regex '.*/\([^/]*\)/\1\.pdf'แล้วมันก็จะทำงานร่วมกับ findBSD
Stéphane Chazelas

7

ตัวแปรลูปดั้งเดิมของการfind .. -exec sh -c ''ใช้เชลล์สร้างเพื่อให้ตรงกับชื่อฐานและเส้นทางทันทีข้างต้นจะต้องทำด้านล่าง

find foo/ -name '*.pdf' -exec sh -c '
    for file; do 
        base="${file##*/}"
        path="${file%/*}"
        if [ "${path##*/}" =  "${base%.*}" ]; then
            printf "%s\n" "$file" 
        fi
    done' sh {} +

หากต้องการแยกการขยายพารามิเตอร์แต่ละรายการ

  • fileมีพา ธ เต็มของ.pdfไฟล์ที่ส่งคืนจากfindคำสั่ง
  • "${file##*/}"มีเฉพาะส่วนหลัง/IE สุดท้ายเท่านั้นที่เป็นพื้นฐานของไฟล์
  • "${file%/*}"มีเส้นทางจนถึงขั้นสุดท้าย/เช่นยกเว้นส่วนชื่อฐานของผลลัพธ์
  • "${path##*/}"มีส่วนหนึ่งหลังจากที่ผ่านมา/จากpathตัวแปรเช่นเส้นทางโฟลเดอร์ทันทีเหนือ basename ของไฟล์
  • "${base%.*}"มีส่วนหนึ่งของชื่อฐานที่มี.pdfนามสกุลถูกลบ

ดังนั้นหากชื่อไฟล์ที่ไม่มีนามสกุลตรงกับชื่อของโฟลเดอร์ทันทีด้านบนเราจะพิมพ์พา ธ


7

การกลับคำตอบของ Inianคือค้นหาไดเรกทอรีแล้วดูว่าพวกเขามีไฟล์ที่มีชื่อเฉพาะหรือไม่

ต่อไปนี้จะพิมพ์ชื่อพา ธ ของไฟล์ที่พบที่สัมพันธ์กับไดเรกทอรีfoo:

find foo -type d -exec sh -c '
    for dirpath do
        pathname="$dirpath/${dirpath##*/}.pdf"
        if [ -f "$pathname" ]; then
            printf "%s\n" "$pathname"
        fi
    done' sh {} +

${dirpath##*/}$(basename "$dirpath")จะถูกแทนที่ด้วยส่วนชื่อไฟล์ของเส้นทางไดเรกทอรีและจะถูกแทนที่ด้วย

สำหรับผู้ที่ชอบไวยากรณ์ลัดวงจร:

find foo -type d -exec sh -c '
    for dirpath do
        pathname="$dirpath/${dirpath##*/}.pdf"
        [ -f "$pathname" ] && printf "%s\n" "$pathname"
    done' sh {} +

ประโยชน์ของการทำเช่นนี้คือคุณอาจมีไฟล์ PDF มากกว่าไดเรกทอรี จำนวนการทดสอบที่เกี่ยวข้องจะลดลงหากมีข้อ จำกัด ในการสืบค้นด้วยจำนวนที่น้อยกว่า (จำนวนไดเรกทอรี)

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



2

ไม่ได้ระบุ แต่นี่เป็นวิธีการแก้ปัญหาโดยไม่มีการแสดงออกปกติหากใครสนใจ

เราสามารถใช้find . -type fเพื่อรับไฟล์จากนั้นใช้dirnameและbasenameเขียนเงื่อนไข โปรแกรมอรรถประโยชน์มีลักษณะการทำงานต่อไปนี้:

$ find . -type f
./dir2/spam/spam.pdf
./dir2/dir2.tex
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./dir1/dir1.txt

basenameส่งกลับเฉพาะชื่อไฟล์หลังจากล่าสุด/:

$ for file in $(find . -type f); do basename $file; done
spam.pdf
dir2.tex
dir3.pdf
eggs.pdf
dir1.pdf
dir1.txt

dirnameให้เส้นทางทั้งหมดจนถึงจุดสุดท้าย/:

$ for file in $(find . -type f); do dirname $file; done
./dir2/spam
./dir2
./dir3
./dir3/eggs
./dir1
./dir1

ดังนั้นbasename $(dirname $file)ให้ไดเรกทอรีหลักของไฟล์

$ for file in $(find . -type f); do basename $(dirname $file) ; done
spam
dir2
dir3
eggs
dir1
dir1

สารละลาย

รวมด้านบนเพื่อจัดรูปแบบตามเงื่อนไข"$(basename $file)" = "$(basename $(dirname $file))".pdfจากนั้นพิมพ์เฉพาะผลลัพธ์แต่ละรายการfindหากเงื่อนไขนั้นส่งคืนค่าจริง

$ while read file; do if [ "$(basename "$file")" = "$(basename "$(dirname "$file")")".pdf ]; then echo $file; fi done < <(find . -type f)
./dir2/spam/spam.pdf
./dir3/dir3.pdf
./dir3/eggs/eggs.pdf
./dir1/dir1.pdf
./Final Thesis/grits/grits.pdf
./Final Thesis/Final Thesis.pdf

ในตัวอย่างด้านบนเราได้เพิ่มไดเรกทอรี / ไฟล์ที่มีช่องว่างในชื่อเพื่อจัดการกับกรณีดังกล่าว (ขอบคุณ @Kusalananda ในความคิดเห็น)


น่าเสียดายที่จะมีชื่อไฟล์เหมือนFinal Thesis.pdf(มีช่องว่าง)
Kusalananda

@Kusalananda แก้ไขแล้ว
user1717828

0

ฉันใช้bash globbing วนรอบอย่างง่าย ๆ ผ่านการทดสอบสตริงทุกวันในโปรแกรมค้นหา โทรหาฉันอย่างไม่มีเหตุผลและในขณะที่มันอาจเป็นสิ่งที่ไม่ดีเช่นรหัสง่าย ๆ ไม่ได้หลอกลวงให้ฉัน: อ่านและนำกลับมาใช้ใหม่ได้น่าพอใจมาก! อนุญาตให้ฉันแนะนำชุดของ:

•ทุบตี globstar : for f in ** ; do ... **วนซ้ำทุกไฟล์ในไดเรกทอรีปัจจุบันและโฟลเดอร์ย่อยทั้งหมด .. เพื่อตรวจสอบสถานะ globstar ในเซสชันปัจจุบันของคุณ: shopt -p globstar. เมื่อต้องการเปิดใช้งาน shopt -s globstarGLOBSTAR:

• utlity "file" : if [[ $(file "$f") =~ pdf ]]; then ... เพื่อตรวจสอบรูปแบบไฟล์จริงสำหรับpdf - มีความทนทานกว่าการทดสอบเฉพาะส่วนขยายของไฟล์

• basename, dirname : เพื่อเปรียบเทียบชื่อไฟล์กับชื่อของไดเรกทอรีข้างบนทันที basenameคืนค่าชื่อไฟล์ - dirnameคืนค่าเส้นทางไดเรกทอรีทั้งหมด - รวมทั้งสองฟังก์ชั่นเพื่อส่งกลับเฉพาะไดเรกทอรีเดียวที่มีไฟล์ที่ตรงกัน ฉันใส่แต่ละตัวแปร ( _mydirและ_myf ) เพื่อทำการทดสอบอย่างง่ายโดยใช้= ~สำหรับการจับคู่สตริง

การลบย่อยอย่างใดอย่างหนึ่ง: ลบ "จุด" ใด ๆ ในชื่อไฟล์เพื่อหลีกเลี่ยงการจับคู่ชื่อไฟล์กับไดเรกทอรีปัจจุบันซึ่งมีทางลัดด้วย " - ฉันใช้การแทนที่สตริงโดยตรงกับตัวแปร_myf : ${_myf//./}- ไม่สวยงามมาก แต่ใช้งานได้ การแข่งขันในเชิงบวกจะกลับมาแต่ละเส้นทางของไฟล์ - $(pwd)/ร่วมกับเส้นทางแบบเต็มของโฟลเดอร์ปัจจุบันโดยก่อนหน้านี้การส่งออกด้วย:

รหัส

for f in ** ; do
  if [[ $(file "$f") =~ PDF ]]; then
    _mydir="$(basename $(dirname $f))" ; 
    _myf="$(basename $f)" ; 
    [[ "${_myf//./}" =~ "$_mydir" ]] && echo -e "$(pwd)/$f" ; 
  fi ; 
done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.