foreach 루프를 사용하여 디렉터리의 일부 파일을 처리하여 ls 출력을 얻으려고 합니다.
IFS=$'\n'
for i in $(ls -1Atu); do
echo "$i"
done
처음에는 작동한다고 생각했지만 *
or 와 비슷한 이름의 파일을 만들 때 filename*
쉘이 *
와일드카드로 해석되었기 때문에 추가 반복을 추가했습니다.
그러나 이와 같이 ls 출력을 이스케이프하면 for i in "$(ls -1Atu)";
반복이 발생하고 $i는 ls의 출력을 완전히 포함하게 됩니다.
이 문제를 어떻게 해결할 수 있나요? 더 나은 변형이 포함 ls -1Atu
된 결과 파일은 필요에 따라 시간별로 정렬됩니다. 감사해요
답변1
배경 자료:공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?,ls의 출력을 구문 분석하면 안되는 이유
줄바꿈으로 설정하면 IFS
명령 대체 확장 중에 공백과 탭이 아닌 줄바꿈만 구분 기호로 간주됩니다. 귀하의 방법은 개행 문자가 포함된 파일 이름을 지원하지 않습니다. 이는 1 의 근본적인 제한 사항입니다 ls
.
또한 이것이 문제입니다. 이 설정은 IFS
명령이 확장을 대체할 때 발생하는 다른 일(예: 와일드카드 일치)에 영향을 미치지 않습니다. 와일드카드를 끄면 파일 이름에 개행 문자가 포함되지 않는 한 스크립트가 작동합니다.
IFS='
'
set -- *"$IFS"*
if [ -e "$1" ]; then
echo >&2 "There are file names with newlines. I cannot cope with this."
exit 2
fi
set -f
for i in $(ls -1Atu); do
printf '%s\n' "$i"
done
set +f
unset IFS
날짜별로 파일을 나열하는 간단하고 안정적인 방법은 zsh와 같은 순수한 Bourne 스타일 셸이 아닌 다른 것을 사용하는 것입니다.글로벌 예선와일드카드 일치가 정렬되는 방식을 수정합니다.
#!/bin/zsh
files_by_access_time=(*(Doa))
1 일부 구현을 통해 이 문제를 해결할 수 있지만 ls
이식성이 필요한 경우에는 그렇지 않습니다.
답변2
공백 문제 등과 관련된 다양한 이유로 인해 권장되지 않습니다.출력 구문 분석ls
. 또 다른 방법은 find
, sort
, 의 GNU 버전을 사용하는 것입니다 sed
.
find . -mindepth 1 -maxdepth 1 -printf "%A@ %f\0" |
sort -rnz |
sed -z 's/^[0-9.]\+ //'
find
물론ls
파일을 나열하고 필터링하는 것보다 훨씬 더 유연하지만 정렬 자체를 수행하지는 않습니다.- 여기에서는 수정 시간을 초(
%A@
) 단위로 인쇄하고 그 뒤에 파일 이름과 파일 이름을 구분하는 데 사용되는 유일한 안전한 문자인 ASCII NUL 문자를 인쇄합니다. sort -rnz
그런 다음 NUL로 구분된 입력(-z
) 을 사용하여-n
숫자를 내림차순( )으로 정렬합니다( 최신 항목이 먼저 제공되기 때문)-t
.ls
- 그런 다음 일반적으로
sed
초기 시간을 제거하고 NUL로 구분된 행을 다시 처리합니다(-z
).
생성된 출력은 NUL로 구분된 라인의 스트림입니다. Bash(일반 sh 아님)에서는 다음을 통해 변수로 읽을 수 있습니다.
find . -mindepth 1 -maxdepth 1 -printf "%A@ %f\0" |
sort -rnz |
sed -z 's/^[0-9.]\+ //' |
while IFS= read -r -d '' filename
do
# do stuff with filenames
printf "%s\n" "$filename"
done
물론 이것은 조금 더 복잡하지만 더 안전합니다.
답변3
*
정말로 파일 이름을 추가하시겠습니까 ?ls
아니면 실행 권한이 있는 경우 파일 이름으로 끝나는 출력을 제공한다는 의미입니까 ?*
출력 문제만 있는 경우 다음 ls
방법으로 간단히 해결할 수 있습니다.
ls
로 대체되었습니다 \ls
. 이는 앨리어싱되지 않은 버전으로 사용되었으며 ls
출력되지 않습니다.*
답변4
다음과 같이 사용할 수 있습니다.
ls -1Atu | while IFS= read -r entry; do
echo "$entry"
done
이 예에서는 출력이 한 번 생성되고 해당 while read entry
섹션을 통해 출력이 ls
한 줄씩 구문 분석 되므로 for
예의 모든 항목이 $i
단일 라운드에 배치되는 문제가 해결됩니다.