주어진 파일 이전에 정렬된 파일 찾기

주어진 파일 이전에 정렬된 파일 찾기

많은 파일이 포함된 디렉토리가 있습니다.

예:

aaa.txt
bbb.txt
ccc.txt
ddd.txt

임의의 문자열(반드시 파일 이름일 필요는 없음)이 주어지면 해당 문자열 이전에 정렬된 모든 파일을 찾고 싶습니다(일반적인 알파벳 순서로 정렬).

예: 합계를 ccc.txt찾고 싶습니다 .bbb.txtaaa.txt

파일 이름에는 일반 ASCII 문자만 포함됩니다. LC_ALL=C그것은 추측될 수 있다. 숨겨진 파일이 없습니다(로 시작 .).

잠재적인 해결책은 다음과 같을 수 있습니다(만들어진 테스트 포함).

$ find -isnamelessthan ccc.txt
aaa.txt
bbb.txt

이것이 어떻게 달성될 수 있습니까?

답변1

그리고 zsh:

print -rC1 -- **/*(NDe['[[ $REPLY:t < ccc.txt ]]'])

어디:

  • print -rC1 -- printrs에는 aw 및 on 1 Column 매개변수가 있습니다.
  • **/이와 같이 재귀 검색을 위해 모든 수준의 하위 디렉터리(0 포함)를 일치시킵니다 find.
  • (...)일치를 추가로 제한하는 전역 한정자입니다.
    • Nprint: 일치하는 항목이 없을 때 오류가 보고되지 않고 아무것도 인쇄되지 않도록 nullglob:
    • D: dotglob은 와 동일하며 find숨겨진 파일을 제외하지 않습니다.
    • e['code']: 코드를 실행하여 파일을 선택해야 하는지 확인하세요. 여기의 코드는 ail(고려 중인 파일 경로의 기본 이름)의 [[ $REPLY:t < ccc.txt ]]어휘 비교( memcmp()대신 로케일 인식 함수 사용)를 수행합니다 strcoll().t$REPLYccc.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.txtfor 에서와 같이 시작 파일에 상대적인 파일 경로를 ./dir/a.txtNUL 바이트(파일 경로에 나타날 수 없는 유일한 바이트 값)로 구분하여 인쇄합니다.
  • sort -z/globs 를 strcoll()기준으로 목록을 정렬합니다 .ls
  • LC_ALL=C( strcoll()s에서 사용하는 대로)를 (ASCII 기반 시스템에서)로 변환합니다.awk<memcmp()
  • -v RS='\0'입력 R레코드 S구분 기호를 NUL 바이트로 설정합니다( ORS새 줄에는 기본값을 그대로 둡니다).
  • -F/, 약어는 필드 구분 기호 -v FS=/를 로 설정합니다.FS/
  • $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

gawkSté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[@]}

관련 정보