ค้นหาไดเรกทอรีที่ไม่มีไฟล์


58

ใช่ฉันกำลังแยกแยะดนตรีของฉัน ฉันมีทุกอย่างที่จัดไว้อย่างสวยงามในมนต์ต่อไปนี้/Artist/Album/Track - Artist - Title.extและถ้ามีอยู่ปกก็เข้า/Artist/Album/cover.(jpg|png)มา

ฉันต้องการสแกนไดเรกทอรีระดับที่สองทั้งหมดและค้นหาไดเรกทอรีที่ไม่มีปก ในระดับที่สองฉันหมายถึงฉันไม่สนใจว่า/Britney Spears/จะไม่มี cover.jpg แต่ฉันจะสนใจถ้า/Britney Spears/In The Zone/ไม่มี

ไม่ต้องกังวลเกี่ยวกับการดาวน์โหลดหน้าปก (นั่นเป็นโครงการที่สนุกสำหรับฉันในวันพรุ่งนี้) ฉันแค่แคร์เรื่อง bash-fuiness อันรุ่งโรจน์เกี่ยวกับตัวอย่างผกผัน - อิfind


สำหรับใครก็ตามที่สนใจดาวน์โหลดปกที่หายไปเพียงแค่ติดตั้งlaunchpad.net/coverlovinและแทนที่ -print ใน @phoibos answer ด้วย "-exec ./coverlovin.py {} \;"
Dror Cohen

คำตอบ:


81

กรณีที่ 1: คุณรู้จักชื่อไฟล์ที่ต้องการค้นหา

ใช้findกับtest -e your_fileการตรวจสอบว่าไฟล์ที่มีอยู่ ตัวอย่างเช่นคุณค้นหาไดเรกทอรีที่ไม่มีcover.jpgใน:

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print

มันเป็นกรณีที่ละเอียดอ่อน

กรณีที่ 2: คุณต้องการความยืดหยุ่นมากขึ้น

คุณไม่แน่ใจว่าของคดีและนามสกุลของอาจจะjPg, png...

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print

คำอธิบาย:

  • คุณต้องวางไข่เชลล์shสำหรับแต่ละไดเรกทอรีเนื่องจากไม่สามารถใช้การไพพ์ได้find
  • ls -1 "{}"ส่งออกเพียงชื่อไฟล์ของไดเรกทอรีfindในขณะนี้
  • egrep(แทนgrep) ใช้การแสดงออกปกติเพิ่มเติม; -iทำให้ตัวพิมพ์เล็กและพิมพ์เล็ก-qละเว้นผลลัพธ์ใด ๆ
  • "^cover\.(jpg|png)$"เป็นรูปแบบการค้นหา ในตัวอย่างนี้มันตรงเช่นcOver.png, หรือCover.JPG จะต้องหนีมิฉะนั้นจะหมายความว่ามันตรงกับใด ๆของตัวละคร ทำเครื่องหมายจุดเริ่มต้นของบรรทัดจุดสิ้นสุดcover.png.^$

ตัวอย่างรูปแบบการค้นหาอื่น ๆ สำหรับ egrep :

ทดแทนegrep -i -q "^cover\.(jpg|png)$"ส่วนที่มี:

  • egrep -i -q "cover\.(jpg|png)$": ยังตรงcd_cover.png, album_cover.JPG...
  • egrep -q "^cover\.(jpg|png)$"แมตช์cover.png, cover.jpgแต่ไม่ได้Cover.jpg(ความไวกรณีไม่ได้ปิด)
  • egrep -iq "^(cover|front)\.jpg$": ตรงเช่นfront.jpg, Cover.JPGแต่ไม่ได้ Cover.PNG

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสิ่งนี้ให้ดูที่นิพจน์ทั่วไป


สวยงามอย่างยิ่ง - ด้วยปัญหาที่มันไม่ยืดหยุ่นในการเลือกระหว่างเคสหรือส่วนขยายต่าง ๆ (ฉันลองใช้ไวด์การ์ด แต่ไม่ต้องไปเลย) testฉันสงสัยว่ามีเป็นทางเลือกที่ดีกว่าที่จะ
Oli

1
อืมคุณสามารถหาคำตอบได้ด้วยวิธีนี้-exec bash -c '[[ -n $(find "{}" -iname "cover.*") ]]' \;แต่มันค่อนข้างสกปรกในแง่ของการเพิ่มประสิทธิภาพ มันทำงานได้ดี
Oli

ฉันพบว่าคุณสามารถส่งผ่านtestภาระของ-o EXPRESSIONหรือคำสั่ง ... เช่นtest -e "{}/cover.jpg" -o -e "{}/cover.png"ที่ดีกว่าการทำค้นหาเป่าเต็ม แต่ยังคงเป็นกรณีที่สำคัญ
Oli

ฉันควรทราบว่าการเปรียบเทียบประสิทธิภาพของการทดสอบนี้ (การทดสอบสองครั้งต่อความคิดเห็นครั้งล่าสุดของฉัน) กับการแก้ปัญหาอีกสองรายการ (comm'd ค้นหาและ comm'd globbing) นี่คือช้าที่สุด (684ms vs 40ms และ 50ms ตามลำดับ)
Oli

โซลูชันคำตอบดั้งเดิมใช้เวลาประมาณหนึ่งวินาทีและพักในสถานการณ์ที่มี$ชื่อ dir (ตัวอย่างเช่น Ke $ ha)
Oli

12

เรียบง่ายมันเกิดขึ้น ต่อไปนี้จะได้รับรายการของไดเรกทอรีที่มีหน้าปกและเปรียบเทียบกับรายการของไดเรกทอรีระดับที่สองทั้งหมด บรรทัดที่ปรากฏใน "ไฟล์" ทั้งคู่จะถูกระงับไม่ให้ออกจากรายการไดเรกทอรีที่ต้องการการปกปิด

comm -3 \
    <(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
    <(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'

ไชโย

หมายเหตุ:

  • commข้อโต้แย้งของมีดังนี้:

    • -1 ไม่แสดงบรรทัดเฉพาะสำหรับ file1
    • -2 ไม่แสดงบรรทัดเฉพาะสำหรับ file2
    • -3 ไม่แสดงบรรทัดที่ปรากฏในไฟล์ทั้งสอง
  • commใช้ไฟล์เท่านั้นดังนั้น<(...)วิธีการป้อนข้อมูลkooky การทำเช่นนี้จะทำให้เนื้อหาต่าง ๆ ผ่านไฟล์ [ชั่วคราว] จริง

  • commต้องการอินพุตที่เรียงลำดับหรือไม่ทำงานและfindไม่รับประกันการสั่งซื้อ นอกจากนี้ยังต้องไม่ซ้ำกัน การfindดำเนินการครั้งแรกสามารถค้นหาหลายไฟล์เพื่อcover.*ให้มีรายการที่ซ้ำกัน sort -uruffles อย่างรวดเร็วเหล่านั้นลงไปที่หนึ่ง การค้นหาครั้งที่สองจะมีลักษณะเฉพาะเสมอ

  • dirnameเป็นเครื่องมือที่มีประโยชน์ในการรับไฟล์โดยไม่ต้องใช้sed(et al)

  • findและcommทั้งสองยุ่งกับผลลัพธ์ของพวกเขา สุดท้ายจะมีการทำความสะอาดสิ่งขึ้นเพื่อให้คุณจะเหลือsed Artist/Albumสิ่งนี้อาจเป็นหรืออาจไม่เป็นที่ต้องการสำหรับคุณ


2
ครั้งแรกของคุณfindอาจจะง่ายที่จะหลีกเลี่ยงความจำเป็นในการfind ~/Music/ -iname 'cover.*' -printf '%h\n' dirnameแม้ว่าdirnameจะมีประโยชน์ในที่อื่น
ทอม

ขอบคุณ @Tom มันเร็วกว่ามากที่ดึงออกมาทุกที่ (29ms เทียบกับ 734 มิลลิเซคอนในเพลงของฉัน - พบ "อบอุ่น" ทั้งคู่)
Oli

9

นี่เป็นวิธีที่ดีกว่าในการแก้ปัญหาด้วยการปัดเศษมากกว่าการค้นหา

$ cd ... # to the directory one level above the album/artist structure

$ echo */*/*.cover   # lists all the covers

$ printf "%s\n" */*/*.cover # lists all the covers, one per line

ตอนนี้สมมติว่าคุณไม่มีไฟล์หลงทางในโครงสร้างที่ดีนี้ ไดเรกทอรีปัจจุบันประกอบด้วยไดเรกทอรีย่อยของศิลปินเท่านั้นและไดเรกทอรีเหล่านี้มีไดเรกทอรีย่อยของอัลบั้มเท่านั้น จากนั้นเราสามารถทำสิ่งนี้:

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)

<(...)ไวยากรณ์เปลี่ยนตัวกระบวนการทุบตี: จะช่วยให้คุณใช้คำสั่งในสถานที่ของการโต้แย้งไฟล์ มันช่วยให้คุณปฏิบัติต่อการส่งออกของคำสั่งเป็นไฟล์ ดังนั้นเราจึงสามารถเรียกใช้สองโปรแกรมและรับความแตกต่างได้โดยไม่ต้องบันทึกผลลัพธ์ในไฟล์ชั่วคราว diffโปรแกรมคิดว่ามันจะทำงานร่วมกับสองไฟล์ แต่ในความเป็นจริงก็อ่านจากสองท่อ

คำสั่งที่ก่อให้เกิดการป้อนข้อมูลทางด้านขวามือไปdiff, printf "%s\n" */*เพียงแสดงรายการไดเรกทอรีอัลบั้ม คำสั่งทางซ้ายซ้ำผ่าน*.coverเส้นทางและพิมพ์ชื่อไดเรกทอรีของพวกเขา

ทดสอบการทำงาน:

$ find .   # let's see what we have here
.
./a
./a/b
./foo
./foo/bar
./foo/baz
./foo/baz/cover.jpg

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
0a1,2
> a/b
> foo/bar

Aha ที่a/bและไดเรกทอรีไม่มีfoo/barcover.jpg

มีบางกรณีที่มุมแตกเช่นนั้นโดยค่าเริ่มต้น*จะขยายตัวเองหากไม่มีอะไรตรงกัน set -o nullglobนี้สามารถอยู่กับทุบตีของ


ขออภัยในการตอบกลับล่าช้า มันเป็นแนวคิดที่น่าสนใจ แต่: หน้าปกสามารถเป็น png และ jpb และจะไม่commสะอาดกว่านี้diffหรือ
Oli

comm -3 <(printf "%s\n" */*/cover* | sed -r 's/\/[^\/]+$//' | sort -u) <(printf "%s\n" */*)ดูเหมือนว่าการประนีประนอมที่เหมาะสมโดยไม่ต้องมีdiffปุย 's อย่างไรก็ตามมันช้ากว่าการค้นหาสองเท่าของฉันเล็กน้อย
Oli

0
ls --color=never */*.txt | sed 's|/.*||' | sort -u -n > withtxt.txt
ls --color=never -d * | sort -u -n > all.txt
diff all.txt withtxt.txt

จะแสดงไดเรกทอรีทั้งหมดที่ไม่มีไฟล์ txt อยู่ในนั้น

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