많은 파일이 포함된 디렉토리가 있습니다.
예:
aaa.txt
bbb.txt
ccc.txt
ddd.txt
임의의 문자열(반드시 파일 이름일 필요는 없음)이 주어지면 해당 문자열 이전에 정렬된 모든 파일을 찾고 싶습니다(일반적인 알파벳 순서로 정렬).
예: 합계를 ccc.txt
찾고 싶습니다 .bbb.txt
aaa.txt
파일 이름에는 일반 ASCII 문자만 포함됩니다. LC_ALL=C
그것은 추측될 수 있다. 숨겨진 파일이 없습니다(로 시작 .
).
잠재적인 해결책은 다음과 같을 수 있습니다(만들어진 테스트 포함).
$ find -isnamelessthan ccc.txt
aaa.txt
bbb.txt
이것이 어떻게 달성될 수 있습니까?
답변1
그리고 zsh
:
print -rC1 -- **/*(NDe['[[ $REPLY:t < ccc.txt ]]'])
어디:
print -rC1 --
print
r
s에는 aw 및 on1
C
olumn 매개변수가 있습니다.**/
이와 같이 재귀 검색을 위해 모든 수준의 하위 디렉터리(0 포함)를 일치시킵니다find
.(...)
일치를 추가로 제한하는 전역 한정자입니다.N
print
: 일치하는 항목이 없을 때 오류가 보고되지 않고 아무것도 인쇄되지 않도록 nullglob:D
: dotglob은 와 동일하며find
숨겨진 파일을 제외하지 않습니다.e['code']
: 코드를 실행하여 파일을 선택해야 하는지 확인하세요. 여기의 코드는 ail(고려 중인 파일 경로의 기본 이름)의[[ $REPLY:t < ccc.txt ]]
어휘 비교(memcmp()
대신 로케일 인식 함수 사용)를 수행합니다strcoll()
.t
$REPLY
ccc.txt
GNU 시스템에서는 (모든 셸에서) 다음 명령을 사용하여 유사한 작업을 수행할 수 있습니다.
find . -mindepth 1 -printf '%P\0' | sort -z |
LC_ALL=C gawk -v RS='\0' -F/ '$NF < "ccc.txt"'
어디:
-mindepth 1
, 시작 파일(.
)을 제외합니다.! -name .
다른 시작 파일²로 확장되지는 않지만 표준을 사용할 수도 있습니다 .-printf '%P\0'
dir/aaa.txt
for 에서와 같이 시작 파일에 상대적인 파일 경로를./dir/a.txt
NUL 바이트(파일 경로에 나타날 수 없는 유일한 바이트 값)로 구분하여 인쇄합니다.sort -z
/globs 를strcoll()
기준으로 목록을 정렬합니다 .ls
LC_ALL=C
(strcoll()
s에서 사용하는 대로)를 (ASCII 기반 시스템에서)로 변환합니다.awk
<
memcmp()
-v RS='\0'
입력R
레코드S
구분 기호를 NUL 바이트로 설정합니다(ORS
새 줄에는 기본값을 그대로 둡니다).-F/
, 약어는 필드 구분 기호-v FS=/
를 로 설정합니다.F
S
/
$NF < "ccc.txt"
: 마지막 필드를"ccc.txt"
어휘와 비교하고 true인 경우 기본 작업({print}
약어{print $0}
)을 실행하여 레코드를 인쇄합니다.
조건자의 경우 -isnamelessthan
find
다음을 수행할 수 있습니다(zsh에서).
alias -g -- -isnamelessthan='-exec zsh -c "[[ \$1:t < \$2 ]]" zsh {}'
다음과 같이 사용됩니다:
find . -isnamelessthan ccc.txt ';' -print
(각 파일을 확인하기 위해 하나의 인스턴스를 실행하므로 효율적이지 않습니다 zsh
.)
¹ glob 자체는 로케일에 따라 정렬되어 있으므로 다음을 사용하십시오.strcoll()
find /path/to/dir ! -name dir
² 내부적으로 호출되는 파일을 제외하기 때문에 이 작업을 수행 할 수는 없지만 dir
수행할 수는 있습니다 find /path/to/dir/. ! -name .
.
답변2
파일 이름에 개행 문자가 포함되어 있지 않다고 가정하고 awk를 사용하십시오.
$ printf '%s\n' * | awk '$0 >= "ccc.txt"{exit} 1'
aaa.txt
bbb.txt
답변3
test
시스템의 유틸리티가 한 문자열이 다른 문자열 앞에 오는지 여부를 확인하는 비표준 연산자를 지원하는 경우 <
다음 명령과 함께 사용할 수 있습니다 find
.
find . -exec test {} '<' ./ccc.txt \; -print
또는,
find . -exec [ {} '<' ./ccc.txt ] \; -print
여기서는 현재 디렉터리를 기준으로 파일의 경로 이름을 사용하여 비교합니다. 다른 경로 이름에도 동일하게 적용되기 때문입니다. 이 <
연산자는 로 인용되거나 '<'
쉘 이 이를 리디렉션 연산자로 해석하지 못하도록 해야 합니다 "<"
.\<
테스트가 성공하면 -print
조건자는 경로 이름을 출력합니다.
검색을 일반 파일로만 제한하고, 하위 디렉터리로 내려가지 않고, 숨겨진 이름(또는 생각해 낼 수 있는 다른 기준)을 피하는 등 추가 테스트를 추가하세요.
find . ! -path . -prune ! -name '.*' -type f -exec [ {} '<' ./ccc.txt ] \; -print
답변4
gawk
Stéphane Chazelas와 Ed Morton은 awk
문제를 한 줄로 우아하게 해결하는 듯한 훌륭한 답변을 사용하고 게시했습니다.
그러나 미래의 프로그래머는 awk
이러한 솔루션을 자세히 이해하는 방법을 알아야 합니다. 그래서 제 경우에는 간단한 for 루프를 사용하는 것이 더 낫다고 생각합니다.
가장 좋은 설명이 있으므로 Stéphane의 답변을 받아들이고 여기에 내 솔루션도 남겨 두겠습니다.
target="ccc.txt"
arr=()
target="ccc.txt"
arr=()
for f in * ; do
# You can compare all string (also non-numeric) using <
# -f handles the case when there are no files present
if [[ -f $f && $f < $target ]] ; then arr+=("$f") ; fi
done
echo ${arr[@]}