여러 가지 가지치기가 포함된 복잡한 찾기 명령

여러 가지 가지치기가 포함된 복잡한 찾기 명령

예를 들어 여러 시작 지점이 있는 디렉터리 트리를 탐색해야 하지만 다음과 같이 find ./User1 ./User2 ./User3이러한 시작 지점에 존재하는 특정 디렉터리 이름만 처리해야 합니다. ./User1/Documents ./User1/Pictures /.User2/Documents건너뛰고 싶어요그리고 prune지정된 모든 출처 목록에 없는 기타 하위 폴더.

-notpruneand 및 & \(의 다양한 조합을 사용한 모든 시도 는 \)실패했습니다.

시작점은 런타임 시 스크립트에서 결정됩니다. 최후의 수단으로 User?목록을 반복하고 더 큰 시작점 목록(필요한 디렉터리 이름이 추가됨)을 생성하여 시작점 목록을 구축할 수 있었습니다.

제외 ./User?/NotInList되어야 하지만 제외 ./User?/InList/NotInList될 수도 있습니다.

[전체 상황: 데이터 복구를 수행 중이며 UsersMicrosoft Windows 폴더의 "중요" 파일을 열거하고 싶습니다. 폴더를 Public건너 뛰고 Default(그래서 명령의 시작점으로 나머지 폴더 문자열을 만들었습니다 find) Pictures Documents각 사용자의 etc. 폴더를 선택/드롭하는 대신 각 사용자의 AppDataetc. Contacts폴더만 선택/드롭해야 합니다. 거의 중요하지 않습니다. ]

답변1

그리고 zsh:

users=(User1 User2)
# or users=($(<userlist.txt))
# if userlist.txt contains a list of users as separate words

dirs=(Documents Pictures ...)

find ./$^users/$^dirs

fish같은

set users User1 User2 ...
set dirs Documents Pictures
find ./$users/$dirs

fish또는 에서는 zsh -o rcexpandparam중괄호 확장을 사용하여 배열이 확장됩니다. 에서는 zsh단일 배열 확장에 대해 이 구문이 활성화됩니다 $^array.rcexpandparam

여기 인용문은 rc약간 오해의 소지가 있습니다. / (여기서 (1 2))는 이와 같이 확장되지만 $array^string( 이것이 rc와 유사한 유형의 확장이 에서 선택된 이유입니다 ), 배열을 함께 연결하는 데는 작동하지 않습니다. In (동일 )은 동일한 크기의 배열에서만 작동하며 요소를 하나씩 연결합니다( goes , is not ).rcesarray{1,2}stringzsh^$^arrayrc$array1^$arrat2$array1$array2(1 2)^(a b)1a 2b1a 1b 2a 2b

이것들은 그렇지 않습니다.와일드카드, 파일 존재 여부에 관계없이 User1/Documents전달됩니다 . find실제로 존재하는 파일이나 디렉터리 목록을 전달합니다.존재하다, 에서는 zsh할 수 있습니다

find ./$^users/$^dirs(N)

이 패스는 (N)이 배열 곱셈의 모든 요소 결과에 glob 한정자를 추가하며 두 가지 효과가 있습니다.

  • 전역 변수로 만듭니다. 즉, 일치하는 파일로 확장됩니다.
  • glob이 어떤 파일과도 일치하지 않는 경우(와일드카드가 없기 때문에 0 또는 1만 일치할 수 있음) 결과 glob은 빈 상태로 확장됩니다.

아니면 다음으로 갈 수 있습니다.전반적인 상황모든 방법:

find (User1|User2)/(Documents|Pictures)

또는 귀하의 예를 바탕으로:

set -o extendedglob # best in ~/.zshrc
find ./^(#i)(Default|Public)/(Documents|Pictures)

또는 배열을 기반으로 glob을 생성합니다.

find (${(j:|:)~${(b)users}})/${(j:|:)~${(b)dirs}}

어디:

  • (b)배열 요소에 와일드카드 문자가 있는 경우 이를 따옴표로 묶습니다.
  • (j:|:)그들과 합류|
  • ~확장 시 와일드카드 켜기

모든 파일 이름을 사용할 수 있습니다( -제한 사항으로 시작하는 파일 이름은 제외 find).

find특히 임의의 파일 이름을 허용하려는 경우 자체적으로 사용하기가 매우 까다로울 수 있습니다 . 하지만 ...)과 Default같이 비교적 순한 분들은 Public다음을 시도해 볼 수 있습니다.

LC_ALL=C find . -name . -o -path './*/*' \( ! -path './*/*/*' \
  ! -name Documents ! -name Pictures -prune -o -print \) -o   \
  ! -name Default ! -name Public -o -prune

(이런 종류의 일은 나에게 두통을 준다.)

이것은 대소 문자를 구분하지 않는 일치를 원하거나 GNU (당신이 사용하는 것 같음) 또는 호환을 사용하는 경우 find사용할 수 있는 표준 구문을 사용하는 것입니다 .-name '[pP][uU][bB][lL][iI][cC]'find-iname Public

답변2

귀하가 직접 게시한 답변을 보면 현재 디렉토리에서 또는 을 제외한 모든 디렉토리가 있고 , , 또는 중 하나인 디렉토리를 선택하려고 합니다../x/yxPublicDefaultyDocumentsPicturesMy Documents

extglobBash에서 활성화되면 glob을 사용하여 이 작업을 수행할 수 있습니다. (ksh에서는 모드가 명령줄에 직접 있을 때 기본값이며, Zsh에서는 를 사용해야 합니다 setopt kshglob.)

반면:

$ mkdir -p {Someuser,"other user",Public,Default}/{Pictures,"My Documents",Uselessdir}

여기 있는 glob은 원하는 작업을 수행해야 합니다.

$ shopt -s extglob
$ printf "%s\n" ./!(Public|Default)/@(Documents|My Documents|Pictures)
./other user/My Documents
./other user/Pictures
./Someuser/My Documents
./Someuser/Pictures

그래서, 당신은 달릴 수 있어야합니다

shopt -s extglob
find ./!(Public|Default)/@(Documents|My Documents|Pictures) ...

glob은 존재하지 않는 디렉토리로 확장되지 않으므로 디렉토리 find가 존재하지 않는다고 해도 오류가 발생하지 않습니다 ./Someuser/Pictures.

shopt -s nocaseglob대소문자를 구분하지 않고 일치하려면 추가하세요.

답변3

bash중괄호 확장 사용에 대한 @StephaneChazelas의 의견을 바탕으로 파일 이름의 공백을 처리하고 사용자가 한 명인 경우와 같은 극단적인 경우를 처리하는 솔루션은 다음과 같습니다 .

중괄호 확장에 사용되는 문자열은 변수에 저장되므로 eval실제로 확장하려면 해당 문자열을 사용해야 합니다.

# escape spaces with backslash
directories=Documents,Pictures,My\ Documents

# get usernames to use with expansion
# and escape any spaces using sed
user_folders=$(find . -maxdepth 1 -mindepth 1 -not -iname Public -a -not -iname Default -type d -printf "%f," | \
sed 's| |\\ |g' )   

# remove trailing comma from last user's name
user_folders=${user_folders::-1}    

# only use brace expansion if there is more than one user; if there's only
# one value it won't expand and we'll be left with braces
[[ $user_folders = *,* ]] && user_folders="{$user_folders}"

find_string="find $user_folders/{$directories}"
eval "$find_string"

관련 정보