이름이 특정 패턴과 일치하는 파일을 포함하는 서브 디렉토리 목록을 얻으려면 어떻게해야합니까?
더 구체적으로 말하면, 파일 이름에 ‘f’라는 문자가있는 파일이 들어있는 디렉토리를 찾고 있습니다.
이상적으로는 목록에 중복이없고 파일 이름이없는 경로 만 포함됩니다.
답변
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq
위의 내용은 현재 디렉토리 ( .
) 아래 일반 파일 ( -type f
)이고 f
이름 ( -name '*f*'
)에 있는 모든 파일을 찾습니다 . 다음으로 sed
파일 이름을 제거하고 디렉토리 이름 만 남겨 둡니다. 그런 다음 디렉토리 목록이 정렬 sort
되고 ( uniq
) 중복 항목이 제거됩니다 ( ).
이 sed
명령은 단일 대체품으로 구성됩니다. 정규 표현식 /[^/]+$
과 일치하는 항목을 찾고 일치하는 항목을 아무것도없는 것으로 바꿉니다. 달러 기호는 줄의 끝을 의미합니다. [^/]+'
슬래시가 아닌 하나 이상의 문자를 의미합니다. 따라서 /[^/]+$
마지막 슬래시에서 줄 끝까지의 모든 문자를 의미합니다. 즉, 전체 경로의 끝에있는 파일 이름과 일치합니다. 따라서 sed 명령은 파일 이름을 제거하고 파일이 있던 디렉토리의 이름은 변경하지 않습니다.
단순화
많은 현대 sort
명령 -u
은 uniq
불필요한 플래그를 지원합니다 . GNU sed의 경우 :
find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u
그리고 MacOS sed의 경우 :
find . -type f -name '*f*' | sed -E 's|/[^/]+$||' |sort -u
또한 find
명령이 지원하는 find
경우 디렉토리 이름을 직접 인쇄 할 수 있습니다. 이렇게하면 다음이 필요하지 않습니다 sed
.
find . -type f -name '*f*' -printf '%h\n' | sort -u
보다 강력한 버전 (GNU 도구 필요)
위의 버전은 줄 바꿈이 포함 된 파일 이름으로 혼동됩니다. 보다 강력한 솔루션은 NUL 종료 문자열을 정렬하는 것입니다.
find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'
답변
이것을 시도해보십시오.
find / -name '*f*' -printf "%h\n" | sort -u
답변
이를 위해 사용할 수있는 방법은 기본적으로 두 가지가 있습니다. 하나는 문자열을 구문 분석하고 다른 하나는 각 파일에서 작동합니다. 문자열을 파싱하는 것은 grep
, 와 같은 도구를 사용 sed
하거나 awk
분명히 더 빠를 것입니다. 그러나 여기에는 두 가지 방법을 모두 “프로파일”하는 방법과 둘 다를 보여주는 예가 있습니다.
샘플 데이터
아래 예에서는 다음 데이터를 사용합니다.
$ touch dir{1..3}/dir{100..112}/file{1..5}
$ touch dir{1..3}/dir{100..112}/nile{1..5}
$ touch dir{1..3}/dir{100..112}/knife{1..5}
에서 일부 *f*
파일을 삭제 하십시오 dir1/*
.
$ rm dir1/dir10{0..2}/*f*
접근법 # 1-문자열을 통한 구문 분석
여기서는 다음과 같은 도구 find
인 grep
, 및 을 사용합니다 sort
.
$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/
접근법 # 2-파일을 사용한 구문 분석
이번에는 dirname
대신에 사용할 도구를 제외하고 이전과 동일한 도구 체인 입니다 grep
.
$ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5
./dir1/dir103
./dir1/dir104
./dir1/dir105
./dir1/dir106
./dir1/dir107
참고 : 위의 예제는 head -5
이러한 예제에서 처리하는 출력량을 제한하기 위해 사용 됩니다. 전체 목록을 얻으려면 일반적으로 삭제됩니다!
결과 비교
우리는 time
두 가지 접근법을 살펴볼 수 있습니다 .
dirname
real 0m0.372s
user 0m0.028s
sys 0m0.106s
grep
real 0m0.012s
user 0m0.009s
sys 0m0.007s
따라서 가능한 경우 항상 문자열을 처리하는 것이 가장 좋습니다.
대체 문자열 파싱 방법
grep & PCRE
$ find . -type f -name '*f*' | grep -oP '^.*(?=/)' | sort -u
sed
$ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u
어 wk
$ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u
답변
여기 내가 유용하다고 생각하는 것이 있습니다.
find . -type f -name "*somefile*" | xargs dirname | sort | uniq
답변
이 답변은 부끄러운 답변을 기반으로합니다. 흥미로운 접근 방식이지만 파일 및 / 또는 디렉토리 이름에 특수 문자 (공백, 반열 …)가있는 경우 제한이 있습니다. 좋은 습관은 사용하는 것 find /somewhere -print0 | xargs -0 someprogam
입니다.
샘플 데이터
아래 예에서는 다음 데이터를 사용합니다.
mkdir -p dir{1..3}/dir\ {100..112}
touch dir{1..3}/dir\ {100..112}/nile{1..5}
touch dir{1..3}/dir\ {100..112}/file{1..5}
touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5}
에서 일부 *f*
파일을 삭제 하십시오 dir1/*/
.
rm dir1/dir\ 10{0..2}/*f*
접근법 # 1-파일을 사용한 구문 분석
$ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5
./dir1/dir 103
./dir1/dir 104
./dir1/dir 105
./dir1/dir 106
./dir1/dir 107
참고 : 위의 예제는 head -5
이러한 예제에서 처리하는 출력량을 제한하기 위해 사용 됩니다. 전체 목록을 얻으려면 일반적으로 삭제됩니다! 또한 echo
사용하려는 명령을 바꾸십시오 .
답변
로 zsh
:
typeset -aU dirs # array with unique values
dirs=(**/*f*(D:h))
printf '%s\n' $dirs