Tìm thư mục KHÔNG chứa tệp


58

Vâng, tôi đang phân loại âm nhạc của mình. Tôi đã sắp xếp mọi thứ đẹp đẽ theo câu thần chú sau: /Artist/Album/Track - Artist - Title.extvà nếu có, cái bìa nằm trong /Artist/Album/cover.(jpg|png).

Tôi muốn quét qua tất cả các thư mục cấp hai và tìm những thư mục không có bìa. Ở cấp độ thứ hai, tôi có nghĩa là tôi không quan tâm nếu /Britney Spears/không có cover.jpg, nhưng tôi sẽ quan tâm nếu /Britney Spears/In The Zone/không có.

Đừng lo lắng về việc tải xuống bìa (đó là một dự án thú vị cho tôi vào ngày mai) Tôi chỉ quan tâm đến bash-fuiness vinh quang về một findví dụ ngược .


đối với bất kỳ ai quan tâm đến việc tải xuống các trang bìa bị thiếu, chỉ cần cài đặt launchpad.net/coverlovin và thay thế -print trong câu trả lời @phoibos bằng "-exec ./coverlovin.py {} \;"
Dror Cohen

Câu trả lời:


81

Trường hợp 1: Bạn biết tên tệp chính xác cần tìm

Sử dụng findvới test -e your_fileđể kiểm tra nếu một tập tin tồn tại. Ví dụ: bạn tìm các thư mục không có cover.jpgtrong đó:

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

Đó là trường hợp nhạy cảm mặc dù.

Trường hợp 2: Bạn muốn linh hoạt hơn

Bạn không chắc chắn về trường hợp này và tiện ích mở rộng có thể jPg, png...

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

Giải trình:

  • Bạn cần sinh ra một shell shcho mỗi thư mục vì không thể sử dụng đường ống khi sử dụngfind
  • ls -1 "{}"chỉ xuất ra tên tệp của thư mục findhiện đang duyệt
  • egrep(thay vì grep) sử dụng các biểu thức chính quy mở rộng; -ilàm cho trường hợp tìm kiếm không nhạy cảm, -qlàm cho nó bỏ qua bất kỳ đầu ra nào
  • "^cover\.(jpg|png)$"là mẫu tìm kiếm. Trong ví dụ này, nó khớp với cOver.png, ví dụ , Cover.JPGhoặc cover.png. Các .phải được thoát ra nếu không nó có nghĩa là nó phù hợp với bất kỳ nhân vật. ^đánh dấu sự bắt đầu của dòng, $kết thúc của nó

Các ví dụ mẫu tìm kiếm khác cho egrep :

Thay thế egrep -i -q "^cover\.(jpg|png)$"một phần với:

  • egrep -i -q "cover\.(jpg|png)$": Cũng phù hợp cd_cover.png, album_cover.JPG...
  • egrep -q "^cover\.(jpg|png)$": Khớp cover.png, cover.jpgnhưng KHÔNG Cover.jpg(độ nhạy trường hợp không được tắt)
  • egrep -iq "^(cover|front)\.jpg$": Phù hợp với ví dụ front.jpg, Cover.JPGnhưng không Cover.PNG

Để biết thêm thông tin về điều này, hãy xem Biểu thức chính quy .


Hoàn toàn đẹp - với vấn đề là không linh hoạt để chọn giữa các trường hợp hoặc các tiện ích mở rộng khác nhau (Tôi đã thử một ký tự đại diện nhưng không được). Tôi tự hỏi nếu có một sự thay thế tốt hơn để test.
Oli

1
Hmm, bạn có thể lồng tìm thấy với điều này -exec bash -c '[[ -n $(find "{}" -iname "cover.*") ]]' \;nhưng điều đó khá bẩn thỉu về mặt tối ưu hóa. Nó làm việc mặc dù.
Oli

Tôi thấy rằng bạn có thể vượt qua testtải -o EXPRESSIONcho các truy vấn OR ... ví dụ: test -e "{}/cover.jpg" -o -e "{}/cover.png"tốt hơn so với thực hiện tìm kiếm toàn diện nhưng nó vẫn phân biệt chữ hoa chữ thường.
Oli

Tôi nên lưu ý rằng so sánh hiệu suất của điều này (hai bài kiểm tra, theo nhận xét cuối cùng của tôi) so với hai giải pháp khác (tìm và chia sẻ toàn cầu) thì đây là cách chậm nhất (lần lượt là 684ms so với 40ms và 50ms)
Oli

Giải pháp trả lời ban đầu mất hơn một giây và phá vỡ trong các trường hợp có $tên thư mục (ví dụ: Ke $ ha).
Oli

12

Đơn giản, nó transpires. Sau đây nhận được một danh sách các thư mục có bìa và so sánh với danh sách tất cả các thư mục cấp hai. Các dòng xuất hiện trong cả hai "tệp" đều bị chặn, để lại một danh sách các thư mục cần bìa.

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

Hoan hô.

Ghi chú:

  • commĐối số của như sau:

    • -1 triệt tiêu các dòng duy nhất cho file1
    • -2 triệt tiêu các dòng duy nhất cho file2
    • -3 ngăn chặn các dòng xuất hiện trong cả hai tập tin
  • commchỉ lấy các tập tin, do đó <(...)phương thức nhập liệu kooky . Điều này dẫn nội dung thông qua một tập tin [tạm thời] thực sự.

  • commcần sắp xếp đầu vào hoặc nó không hoạt động và findkhông có nghĩa là đảm bảo một đơn đặt hàng. Nó cũng cần phải là duy nhất. findHoạt động đầu tiên có thể tìm thấy nhiều tệp cover.*để có thể có các mục trùng lặp. sort -unhanh chóng xù những người xuống một. Phát hiện thứ hai luôn luôn là duy nhất.

  • dirnamelà một công cụ hữu ích để nhận thư mục của tệp mà không cần dùng đến sed(et al).

  • findcommcả hai hơi lộn xộn với đầu ra của họ. Cuối cùng sedlà ở đó để dọn dẹp mọi thứ để bạn còn lại với Artist/Album. Điều này có thể hoặc không thể mong muốn cho bạn.


2
đầu tiên của bạn findcó thể có thể được đơn giản hóa find ~/Music/ -iname 'cover.*' -printf '%h\n', tránh sự cần thiết cho dirname. mặc dù dirnamelà tiện dụng ở nơi khác
Tom

Cảm ơn @Tom, điều đó nhanh hơn rất nhiều khi phát hiện ra mọi nơi (29ms so với 734ms trên thư mục âm nhạc của tôi - cả hai "ấm" tìm thấy)
Oli

9

Điều này là tốt hơn để giải quyết với globalbing hơn với find.

$ 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

Bây giờ giả sử bạn không có tập tin đi lạc trong cấu trúc tốt đẹp này. Thư mục hiện tại chỉ chứa các thư mục con nghệ sĩ và những thư mục này chỉ chứa các thư mục con album. Sau đó chúng ta có thể làm một cái gì đó như thế này:

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

Các <(...)cú pháp là quá trình thay Bash: nó cho phép bạn sử dụng một lệnh ở vị trí của một đối số tập tin. Nó cho phép bạn coi đầu ra của lệnh là một tệp. Vì vậy, chúng tôi có thể chạy hai chương trình và lấy diff của chúng mà không lưu kết quả đầu ra của chúng trong các tệp tạm thời. Các diffchương trình cho rằng nó đang làm việc với hai tập tin, nhưng trong thực tế nó đọc từ hai ống.

Lệnh sản xuất đầu vào tay quyền diff, printf "%s\n" */*, chỉ cần liệt kê các thư mục album. Lệnh bên trái lặp qua các *.coverđường dẫn và in tên thư mục của chúng.

Chạy thử nghiệm:

$ 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/bfoo/barthư mục không có cover.jpg.

Có một số trường hợp góc bị hỏng, như thế theo mặc định *sẽ mở rộng ra nếu nó không khớp với gì. Điều này có thể được giải quyết với Bash's set -o nullglob.


Xin lỗi vì đã trả lời trễ. Đó là một ý tưởng thú vị nhưng: bìa có thể bằng png và jpb và, sẽ không commsạch hơn diff?
Oli

comm -3 <(printf "%s\n" */*/cover* | sed -r 's/\/[^\/]+$//' | sort -u) <(printf "%s\n" */*)có vẻ như một sự thỏa hiệp hợp lý mà không có bất kỳ diffsự vụng về nào. Tuy nhiên, nó chậm hơn một chút so với tìm kiếm kép của tôi.
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

Sẽ hiển thị tất cả các thư mục không có tệp txt trong đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.