예를 들어 여러 시작 지점이 있는 디렉터리 트리를 탐색해야 하지만 다음과 같이 find ./User1 ./User2 ./User3
이러한 시작 지점에 존재하는 특정 디렉터리 이름만 처리해야 합니다. ./User1/Documents
./User1/Pictures
/.User2/Documents
건너뛰고 싶어요그리고 prune
지정된 모든 출처 목록에 없는 기타 하위 폴더.
-not
prune
and 및 & \(
의 다양한 조합을 사용한 모든 시도 는 \)
실패했습니다.
시작점은 런타임 시 스크립트에서 결정됩니다. 최후의 수단으로 User?
목록을 반복하고 더 큰 시작점 목록(필요한 디렉터리 이름이 추가됨)을 생성하여 시작점 목록을 구축할 수 있었습니다.
제외 ./User?/NotInList
되어야 하지만 제외 ./User?/InList/NotInList
될 수도 있습니다.
[전체 상황: 데이터 복구를 수행 중이며 Users
Microsoft Windows 폴더의 "중요" 파일을 열거하고 싶습니다. 폴더를 Public
건너 뛰고 Default
(그래서 명령의 시작점으로 나머지 폴더 문자열을 만들었습니다 find
) Pictures
Documents
각 사용자의 etc. 폴더를 선택/드롭하는 대신 각 사용자의 AppData
etc. 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 ).rc
es
array
{1,2}string
zsh
^
$^array
rc
$array1^$arrat2
$array1$array2
(1 2)^(a b)
1a 2b
1a 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/y
x
Public
Default
y
Documents
Pictures
My Documents
extglob
Bash에서 활성화되면 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"