답변:
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
위의 버전은 줄 바꿈이 포함 된 파일 이름으로 혼동됩니다. 보다 강력한 솔루션은 NUL 종료 문자열을 정렬하는 것입니다.
find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/'
-E
MacOS 용 으로 표시되도록 답변이 업데이트되었습니다 .
이것을 시도해보십시오.
find / -name '*f*' -printf "%h\n" | sort -u
find
은 실제로 매우 희박 합니다. -printf
운영자가 지정 되지 않았습니다 . BSD에서는 작동하지 않습니다 find
. 따라서 "완전히 POSIX 호환"이 아닙니다 . ( sort -u
POSIX에 있습니다.)
이를 위해 사용할 수있는 방법은 기본적으로 두 가지가 있습니다. 하나는 문자열을 구문 분석하고 다른 하나는 각 파일에서 작동합니다. 문자열을 파싱하는 것은 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*
여기서는 다음과 같은 도구 find
인 grep
, 및 을 사용합니다 sort
.
$ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5
./dir1/dir103/
./dir1/dir104/
./dir1/dir105/
./dir1/dir106/
./dir1/dir107/
이번에는 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 /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*
$ 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
사용하려는 명령을 바꾸십시오 .
uniq
믹스에 던지면 이미 서로 인접한 반복되는 줄을 제거하여 많은 도움이됩니다.find . -type f -name '*f*' -printf '%h\0' | uniq -z | sort -zu | tr '\0' '\n'
. 또는 도구가 약간 오래된 경우 uniq에 -z 옵션이 없을 수 있습니다.find . -type f -name '*f*' -printf '%h\n' | uniq | sort -u