파일을 포함하지 않는 디렉토리를 찾으십시오


58

예, 음악을 정리하고 있습니다. 다음과 같은 만트라로 모든 것이 아름답게 배열되어 /Artist/Album/Track - Artist - Title.ext있습니다.있는 경우 덮개가에 /Artist/Album/cover.(jpg|png)있습니다.

두 번째 수준의 모든 디렉토리를 스캔하고 커버가없는 디렉토리를 찾고 싶습니다. 2 단계로, /Britney Spears/cover.jpg 가 없으면 신경 쓰지 않지만 그것을 가지고 있지 않으면 신경 /Britney Spears/In The Zone/쓰지 않습니다.

표지 다운로드에 대해 걱정하지 마십시오 (내일의 재미있는 프로젝트입니다) find.


누락 된 표지를 다운로드하려는 사람은 launchpad.net/coverlovin을 설치 하고 @phoibos 응답의 -print를 "-exec ./coverlovin.py {} \;"로 바꾸십시오.
Dr. Cohen

답변:


81

사례 1 : 당신은 찾을 정확한 파일 이름을 알고

findwith 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하지만 NOT Cover.jpg(대소 문자 구분이 해제되지 않음)
  • egrep -iq "^(cover|front)\.jpg$": 예를 들어, 일치 front.jpg, Cover.JPG하지만 하지 Cover.PNG

이에 대한 자세한 내용은 정규식을 확인하십시오 .


케이스 나 다른 확장 프로그램 중에서 유연하게 선택할 수 없다는 문제와 함께 절대적으로 아름답습니다 (와일드 카드를 사용해 보았습니다). 에 대한 더 나은 대안이 있는지 궁금합니다 test.
Oli

1
흠 당신은 이것으로 발견을 내포 할 수 -exec bash -c '[[ -n $(find "{}" -iname "cover.*") ]]' \;있지만 최적화 측면에서 꽤 불결합니다. 그래도 작동합니다.
Oli

나는 당신이 OR 쿼리 test-o EXPRESSION대한 로드를 전달할 수 있음을 발견했습니다 ... 예 : test -e "{}/cover.jpg" -o -e "{}/cover.png"전체 검색을 수행하는 것보다 낫지 만 여전히 대소 문자를 구분합니다.
Oli

다른 두 솔루션 (comm'd find 및 comm'd globbing)과이 성능 (마지막 주석 당 2 개의 테스트)의 성능을 비교하는 것이 가장 느립니다 (각각 684ms vs 40ms 및 50ms)
Oli

원래의 응답 솔루션은 1 초 이상 걸리며 $dir 이름 (예 : Ke $ ha) 이있는 환경에서 중단됩니다 .
Oli

12

간단합니다. 다음은 표지가있는 디렉토리 목록을 가져 와서 모든 2 차 디렉토리 목록과 비교합니다. 두 "파일"모두에 나타나는 줄은 표시되지 않고 포함해야하는 디렉토리 목록이 남습니다.

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 -u그것들을 빠르게 하나로 뻗습니다. 두 번째 발견은 항상 독특합니다.

  • dirnamesed(et al)에 의지하지 않고 파일의 디렉토리를 얻는 편리한 도구입니다 .

  • findcomm자신의 출력이 조금 지저분한 상표입니다. 마지막 sed으로 작업을 정리하여에 남아 있습니다 Artist/Album. 이것은 당신에게 바람직하거나 바람직하지 않을 수 있습니다.


2
당신의 첫 번째 findfind ~/Music/ -iname 'cover.*' -printf '%h\n'필요를 피하면서 단순화 될 수있다 dirname. 그러나 dirname다른 곳에서는 편리합니다.
Tom

감사합니다 @Tom, 그것은 모든 곳에서 포크 아웃하는 것보다 훨씬 빠릅니다 (내 음악 디렉토리에서 29ms 대 734ms – 둘 다 "따뜻한"발견)
Oli

9

이것은 find보다 globbing으로 해결하는 것이 훨씬 좋습니다.

$ 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프로그램은 두 개의 파일로 작업하고 있다고 생각하지만 실제로는 두 개의 파이프에서 읽습니다.

에 오른쪽 입력을 생산하는 명령은 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/bfoo/bar디렉토리에는 없습니다 cover.jpg.

기본적으로 *아무 것도 일치하지 않으면 자체로 확장 되는 것과 같이 깨진 코너 경우 가 있습니다. 이것은 Bash 's로 해결할 수 있습니다 set -o nullglob.


답변이 늦어 죄송합니다. 흥미로운 아이디어이지만 커버는 png와 jpb로 될 수 있으며 comm보다 깨끗하지 diff않을까요?
Oli

comm -3 <(printf "%s\n" */*/cover* | sed -r 's/\/[^\/]+$//' | sort -u) <(printf "%s\n" */*)diff보풀이 없는 합리적인 타협처럼 보입니다 . 그러나 내 이중 찾기보다 약간 느립니다.
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.