나는 다양한 역사적 이유로 내 시스템 전체에 흩어져 있는 사진을 정리하려고 합니다. 이 작업을 시작하기 위해 명령줄을 사용하여 하나 이상의 jpg 파일이 포함된 모든 디렉터리 목록을 작성하려고 했습니다. 다른 이미지 파일 형식을 찾는 것에 대해 걱정할 필요는 없지만 jpg가 대문자와 소문자로 표시되도록 허용해야 합니다.
각 디렉토리 이름이 최종 목록에 한 번만 나타나기를 원합니다. 예를 들어 다음 디렉터리가 있는 경우 각 디렉터리에는 하나 이상의 jpg 또는 JPG 파일이 포함됩니다.
~Mike/Pictures
~Mike/Pictures/London/Olympics
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Photos
~Mike/Family History/Swaine
결과가 디렉터리당 한 번만 나열되기를 원합니다. 포함된 이미지 파일 수에 관계없이 먼저 정렬한 다음 파일에 쓰는 것이 좋습니다.
~Mike/Family History/Swaine
~Mike/Photos
~Mike/Pictures
~Mike/Pictures/London
~Mike/Pictures/London/Holiday
~Mike/Pictures/London/Olympics
내 명령줄 기술은 아직 이 수준에 미치지 못합니다! 여러 가지 간단한 형태의 개별 명령을 사용할 수 있지만 일단 복잡해지거나 파이프를 연결해야 하면 문제가 발생하는 경향이 있습니다.
답변1
JPEG 이미지 파일의 접미사는 .jpg
또는 다음과 같다고 가정합니다 .JPG
.
find "$HOME" -type f \( -name '*.jpg' -o -name '*.JPG' \) \
-exec sh -c 'for d; do dirname "$d"; done' sh {} + | sort -u -o jpeg_dirs.txt
이는 개행 문자가 포함된 펑키한 디렉토리 이름이 없다는 점에 달려 있습니다.
GNU 사용 find
:
find "$HOME" -type f \( -name '*.jpg' -o -name '*.JPG' \) -printf '%h\n' | sort -u -o jpeg_dirs.txt
이 find
명령은 홈 디렉토리에서 모든 JPEG 이미지를 찾고 해당 이미지가 있는 디렉토리의 이름을 인쇄합니다. 이 디렉터리 이름 목록을 가져와 sort -u
정렬하고 중복 항목을 제거합니다. 결과는 jpeg_dirs.txt
현재 디렉터리의 파일 에 기록됩니다 .
2021년 초(3.3년 후)에 이것을 되돌아보면 위의 해결책이 그 자체로는 틀린 것은 아니지만 약간 거꾸로 되었기 때문에 조금 움츠러듭니다. 또한 "좋은 파일 이름"(개행 없음)에 대한 명백한 가정을 합니다.
find
디렉토리를 검색 할 때 위에서 했던 것처럼 일반 파일을 검색하지 마세요. 디렉토리가 있으면 각 디렉토리를 조사하여 일치하는 파일이 있는지 *.jpg
또는 *.JPG
다른 파일 이름 접미사가 쉽게 추가되는지 확인할 수 있습니다.
find "$HOME" -type d -exec bash -O nullglob -O dotglob -O extglob -c '
for dirpath do
set -- "$dirpath"/*.@(jpg|JPG)
[ "$#" -eq 0 ] || printf "%s\n" "$dirpath"
done' bash {} +
이는 홈 디렉터리에서 아래쪽으로 각 디렉터리를 살펴보고 *.@(jpg|JPG)
각 디렉터리에서 와일드카드 패턴을 확장하려고 시도합니다. 이 패턴은 두 개의 별도 패턴으로 작성될 수도 *.jpg
있으며 *.JPG
우리가 찾고 있는 모든 파일과 일치할 수도 있습니다. 이름이 일치하면 이것이 출력하려는 이름의 디렉터리라고 가정합니다. 이는 다음을 포함하는 디렉토리에 대해 오탐지를 제공합니다.하위 디렉토리이 접미사로.
내부 스크립트를 실행하기 위해 우리가 가지고 있는 셸 옵션을 사용하면 bash
숨겨진 이름을 일치시킬 수 있고( dotglob
), 확장되지 않은 채로 남아 있는 대신 아무것도 일치하지 않을 때 globbing 패턴이 완전히 사라지도록 허용하고( nullglob
), ksh
-inspired 를 사용하여 globbing 패턴을 확장할 수 있습니다 @(...|...)
.
zsh
쉘 사용 :
typeset -U list=(~/**/*.(jpg|JPG)(.DN:h))
print -rC1 $list
list
그러면 고유한 요소만 저장하는 속성을 가진 배열 변수가 생성됩니다 . 확장된 파일 이름 와일드카드 패턴의 결과로 초기화됩니다. 이 패턴은 홈 디렉터리 또는 그 아래의 모든 JPEG 이미지 파일과 일치하며, :h
마지막 패턴은 생성된 경로 이름에서 실제 파일 이름을 제거합니다. .
패턴이 일반 파일에만 일치하도록 하며 D
및 는 N
.dotglob
nullglob
bash
답변2
이를 수행하는 쉬운 방법은 모든 .jpg
파일을 나열한 다음 파일의 기본 이름(마지막 슬래시 다음 부분)을 제거하고 중복 항목을 제거하는 것입니다. 를 사용하면 sed
마지막 슬래시 뒤의 각 줄 부분을 제거할 수 있습니다. 라는 중복 항목을 제거하는 명령이 있지만 uniq
입력이 정렬되어 있다고 가정합니다. 어쨌든 정렬이 필요한 경우 sort
고유하게 만들 수 있습니다.
find ~Mike -iname '*.jpg' | sed 's!/[^/]*$!!' | sort -u >directories_with_jpeg_files.txt
이는 관련된 디렉터리나 파일 이름에 개행 문자가 없다고 가정합니다. 줄바꿈이 포함된 파일 이름은 일반적인 상황에서는 표시되지 않지만, 파일 이름이 공격자에 의해 선택될 수 있는지 주의하십시오(예를 들어, 서버에 업로드된 파일을 처리 중이고 업로더가 파일 이름을 선택할 수 있는 경우).
JPEG 파일이 많이 포함된 디렉터리가 있고 그렇지 않은 디렉터리가 많지 않은 경우 이 방법을 사용하면 중복 파일을 보고하는 데 많은 시간이 걸릴 수 있습니다. find가 디렉토리에서 무언가를 찾으면 바로가기를 알려줄 방법이 없습니다. 그러나 찾기를 디렉터리로 제한하고 각 디렉터리에서 JPEG 파일을 검색하도록 지시할 수 있습니다. 그러나 이로 인해 JPEG 파일이 포함되지 않은 디렉터리의 비용이 증가하므로 JPEGless 디렉터리가 많으면 성능이 저하될 수 있습니다.
find ~Mike -type d -exec sh -c '
for d do
set -- "$d/*.[Jj][Pp][Gg]";
if [ -e "$1" ]; then printf %s\\n "$d"; fi
done
' sh {} + | sort -u >directories_with_jpeg_files.txt
또는 zsh에서 **
와일드카드를 사용하여 디렉터리를 반복적으로 탐색하여 (#i)
후속 경로 구성 요소를 대소문자를 구분하지 않고 일치시켜 전체 디렉터리 트리 등에서 패턴을 일치 **/(#i)*.jpg
시킬 수 있습니다 . 디렉토리 부분을 추출하려면 glob 한정자에 기록 수정자를 추가하세요 . 이를 배열 변수에 채우고 매개변수 확장 플래그를 사용하여 배열의 고유 요소를 추출합니다.*.jpg
*.JPG
.Jpg
h
dirs=(…)
u
set -o extendedglob # for (#i); best in ~/.zshrc
dirs=(~Mike/**/(#i)*.jpg(:h))
print -lr -- ${(u)dirs} >directories_with_jpeg_files.txt
위의 디렉터리별 확인 방법과 동등한 것은 e
glob 한정자를 사용하는 것입니다.
print -lr ~Mike/**/*(/e\''set -- $REPLY/*.(#i)jpg(N[1]); (($# != 0))'\') >directories_with_jpeg_files.txt
답변3
find . -iname '*.jpg' -execdir sh -c 'pwd' _ {} + | sort -u > dirs_with_jpegs.txt
find
지원을 구현한다고 가정하면 제대로 작동할 것입니다 -execdir
(아마도 그렇습니다). -execdir
찾은 파일이 있는 디렉터리에서 명령을 실행합니다. 이 예에서는 pwd
디렉토리 이름을 인쇄하는 명령을 실행합니다. sh -c
명령을 스트립 매개변수로 래핑합니다 . (일부(모두?) 구현에는 현재 디렉터리에 있는 jpeg 파일 목록이 되는 매개변수 대체가 find
필요합니다 . 우리는 해당 목록을 무시하고 디렉터리만 인쇄하려고 합니다.){}