
다음을 포함하는 디렉토리가 제공됩니다.
note 1.txt
, 어제 마지막으로 수정됨note 2.txt
, 마지막 수정은 그저께였습니다note 3.txt
, 오늘 마지막으로 수정됨
배열을 얻는 가장 좋은 방법은 무엇입니까 note 3
note 1
note 2
?
"최고"를 정의하기 위해 저는 효율성과 이식성보다 견고성(macOS의 Zsh 환경에서)에 더 관심이 있습니다.
의도된 사용 사례는 수백 또는 수천 개의 일반 텍스트 파일을 포함하는 디렉터리이지만 (질문을 혼동할 위험이 있음) 이것은 내가 겪고 있는 보다 일반적인 문제, 즉 파일 경로에서 문자열을 실행하는 특정 사례입니다. 가장 좋은 방법은 ls
, find
및 와 같은 명령을 통해 인쇄하는 것입니다 mdfind
.
나는 위의 목적을 달성하기 위해 이 명령을 호출하는 매크로를 사용해 왔습니다.
ls -t | sed -e 's/.[^.]*$//'
결코 실패하지 않지만,
- 그렉의 위키출력을 구문 분석하지 않는 것이 좋습니다
ls
. (분석하다ls
;관행, '5. 절대 하지 마세요' 아래). sed
매개변수 확장이 가능한 곳을 호출하는 것은 비효율적인가요?
정렬되지 않은 목록을 생성하는 인수 확장 find
(파일 경로를 안전하게 구분하려면 개행 대신 문자 사용 )을 사용하여 기본 이름을 추출합니다.NUL
find . -type f -print0 | while IFS= read -d '' -r l ; do print "${${l%.*}##*/}" ; done
하지만 수정된 날짜별로 정렬하려면 호출이 필요한 것 같고 stat
macOS sort
에는 태그가 find
없기 때문입니다.-printf
그렇지 않으면 잘 작동할 수도 있습니다..
마지막으로 Zsh를 사용하십시오.글로벌 예선:
for f in *(om) ; do print "${f%.*}" ; done
이식 가능하지는 않지만 이 마지막 접근 방식이 나에게는 가장 강력하고 효율적인 것 같습니다. 이 올바른지 find
? 단순히 디렉토리에 파일을 나열하는 대신 실제로 검색을 수행할 때 위 명령의 수정된 버전을 사용하면 안되는 이유가 있습니까?
답변1
존재하다 zsh
,
list=(*(Nom:r))
확실히 가장 튼튼합니다.
print -rC1 -- *(Nom:r)
한 줄에 하나씩 인쇄하거나
print -rNC1 -- *(Nom:r)
NUL은 파일 경로에 허용되지 않는 유일한 문자이므로 출력에서 모든 작업을 수행할 수 있도록 NUL로 구분된 레코드로 사용됩니다.
*(N-om:r)
수정 시간을 고려하려면 다음으로 변경하십시오.뒤쪽에심볼릭 링크 확인(대상의 런타임, 그런 심볼릭 링크가 아님 ls -Lt
).
:r
(을 위한뿌리csh
name)은 확장을 제거하는 데 사용되는 기록 수정자( 의 )입니다 . .bashrc
이 옵션이 활성화된 경우에만 빈 문자열이 됩니다 .dotglob
재귀적 으로 실행되도록 변경 **/*(N-om:t:r)
(:t
꼬리(기본 이름), 즉 디렉터리 구성 요소를 제거합니다.
임의의 파일 이름을 사용하여 이를 안정적으로 수행하는 것은 ls
어려울 것입니다.
한 가지 접근 방식은 실행 ls -td -- ./*
(파일 이름 목록이 인수 목록 제한을 준수한다고 가정)하고 해당 출력을 구문 분석하여 각 파일 이름이 로 시작한다는 사실을 기반으로 ./
NUL 구분 목록 또는 셸 인용 목록을 생성하여 이를 셸에 전달하는 것입니다. 그러나 그것은 이식 가능합니다. 또한 perl
또는 에서 도움을 구하지 않는 한 매우 고통스럽습니다 python
.
그러나 신뢰할 수 있는 경우 perl
NUL python
로 구분된 출력을 사용하여 파일 목록을 생성하고 정렬하도록 할 수 있습니다(초 미만의 정밀도를 지원하려는 경우 이식하기가 쉽지 않을 수 있음).
ls -t | sed -e 's/.[^.]*$//'
개행 문자가 포함된 파일 이름에서는 제대로 작동하지 않습니다(IIRC 일부 macOS 버전에는 /etc
기본적으로 이러한 파일 이름이 함께 제공됨). 유효한 문자를 형성하지 않는 바이트 시퀀스가 포함된 파일 이름의 경우에도 실패하거나 .
일치 [^.]
하지 않을 수 있습니다. 하지만 macOS에서는 작동하지 않을 수 있으며 로케일을 C
/ POSIX
로 설정하여 문제를 해결할 수 있습니다 sed
.
.
( )는 s/\.[^.]*$//
모든 문자와 일치하는 정규식 연산자이므로 이스케이프 해야 합니다 . 그렇지 않으면 점이 없는 파일을 foobar
빈 문자열로 변환합니다.
문자열 인쇄에 유의하세요.날것의, 그것은:
print -r -- "$string"
print "$string"
$string
로 시작하는 값에 대해서는 실패하며 -
명령 주입 취약점이 발생하더라도(예: 여기에서 string='-va[$(uname>&2)1]'
무해한 uname
명령을 사용하려고 시도하는 경우) 문자가 포함된 값을 파괴합니다 \
.
당신의:
find . -type f -print0 | while IFS= read -d '' -r l ; do print "${${l%.*}##*/}" ; done
또 다른 문제는 당신이 옷을 벗었다는 것입니다.*
앞으로디렉터리 구성 요소를 제거합니다. 예를 들어 a는 ./foo.d/bar
대체되어 foo
빈 문자열이 bar
됩니다 ../foo
find
다양한 셸에서 출력을 처리하는 안전한 방법은 다음을 참조하세요.찾기 결과를 반복하는 것이 왜 나쁜 습관입니까?
답변2
IMNSHO 견고성과 쉘 스크립팅은 호환되지 않는 개념입니다(IFS는 단지 해킹일 뿐입니다. 죄송합니다). 나는 당신이 원하는 것을 강력한 방식으로 달성하는 방법은 두 가지 밖에 없다고 생각합니다. 정상적인 언어(Python, C 등)로 프로그램을 작성하거나 견고성을 위해 특별히 제작된 도구를 사용하는 것입니다.
csv-nix-tools(*)를 사용하면 다음과 같이 이를 달성할 수 있습니다.
csv-ls -c name,mtime_sec,mtime_nsec |
csv-sort -c mtime_sec,mtime_nsec |
csv-cut -c name |
csv-add-split -c name -e . -n base,ext -r |
csv-cut -c base |
csv-header --remove
꽤 자명합니다.
파일의 기본 이름만 보고 싶은 경우에는 이것으로 충분하지만 일반적으로 방금 얻은 데이터를 사용하여 유용한 작업을 수행하려는 경우가 많습니다. 이것이 바로 싱크 도구의 목적입니다. 현재 csv-exec(각 줄에서 명령 실행), csv-show(사람이 읽을 수 있는 형식으로 데이터 형식 지정) 및 csv-plot(gnuplot을 사용하여 2D 또는 3D 그래픽 생성)의 세 가지가 있습니다.
아직 부족한 부분이 있지만 도구는 사용을 시작할 수 있을 만큼 충분히 좋습니다.
답변3
GNU 도구가 있는 시스템에서 상당히 광범위한 ksh 확장(bash 및 zsh 포함)을 사용하는 모든 셸에서 작동하는 다른 방법이 이미 다루어지지 않았다는 사실에 놀랐습니다.
while IFS= read -r -d ' ' time && IFS= read -r -d '' filename; do
printf 'Filename %q, with epoch time %s\n' "$filename" "$time"
done < <(find . -mindepth 1 -maxdepth 1 -printf '%T@ %P\0' | sort -gz)
작동 방식을 설명하세요.
- 형식
find
문자열은%T@ %P\0
각 파일에 대해 10진수 타임스탬프(선택적 1초 미만 정밀도 포함), 공백, 파일의 기본 이름, NUL을 인쇄합니다. - 에서는 부동 소수점 값의 일반화된 순서가 올바르게 처리되며
sort -gz
구분 기호로 개행 문자 대신 NUL이 예상됩니다.-g
-z
- 에서는
IFS= read -r -d ' ' time && IFS= read -r -d '' filename
첫 번째 공백에서 시간 읽기를 종료하고 첫 번째 NUL에서 파일 이름 읽기를 종료합니다. - 형식 문자열을 사용하여 결과를 인쇄할 때
%q
파일 이름에 있는 인쇄할 수 없는 문자(탭, 줄 바꿈, 캐리지 리턴 등)도 읽을 수 있는 텍스트로 변환합니다 .