for 루프를 어떻게 되돌 립니까?


26

루프를 역순으로 올바르게 수행하는 방법은 for무엇입니까?

for f in /var/logs/foo*.log; do
    bar "$f"
done

파일 이름에서 펑키 한 문자를 깨뜨리지 않는 솔루션이 필요합니다.


5
바로 sort -r앞에 파이프를 for넣거나을 세탁하십시오 ls -r.
David Schwartz

답변:


27

bash 또는 ksh에서 파일 이름을 배열에 넣고 해당 배열을 역순으로 반복하십시오.

files=(/var/logs/foo*.log)
for ((i=${#files[@]}-1; i>=0; i--)); do
  bar "${files[$i]}"
done

위의 코드는 ksh_arrays옵션이 설정되어있는 경우 (zsh 에뮬레이션 모드에있는 경우) zsh에서도 작동 합니다. zsh에는 glob 한정자를 통해 일치하는 순서를 바꾸는 간단한 방법이 있습니다.

for f in /var/logs/foo*.log(On); do bar $f; done

POSIX에는 배열이 포함되어 있지 않으므로 이식성을 원한다면 문자열 배열을 직접 저장하는 유일한 옵션은 위치 매개 변수입니다.

set -- /var/logs/foo*.log
i=$#
while [ $i -gt 0 ]; do
  eval "f=\${$i}"
  bar "$f"
  i=$((i-1))
done

당신이 위치 매개 변수를 사용하면, 당신의 변수에 대한 필요가 없습니다 if; 다만 수행 shift, 사용 $1당신을 호출 bar에, 테스트를 [ -z $1 ]당신의에서 while.
user1404316

@ user1404316 오름차순으로 요소를 반복하는 지나치게 복잡한 방법입니다 . 또한 잘못된 것입니다. 유용한 테스트 도 [ -z $1 ]아닙니다 [ -z "$1" ](매개 변수가 *비어 있거나 문자열 이 아닌 경우 ). 그리고 어쨌든 그들은 여기에서 도움이되지 않습니다 : 문제는 반대 순서로 반복하는 방법입니다.
Gilles 'SO- 악마 그만

이 질문은 특히 / var / log의 파일 이름을 반복한다고 말하므로 매개 변수가 "*"이거나 비어 있지 않다고 가정하는 것이 안전합니다. 그러나 나는 역순으로 수정되었습니다.
user1404316

7

줄 바꿈을 "펑키 문자"로 간주하지 않는 한 다음을 시도하십시오.

ls /var/logs/foo*.log | tac | while read f; do
    bar "$f"
done

줄 바꿈과 함께 작동하는 방법이 있습니까? (드문 일이지만 학습을 위해 올바른 코드를 작성할 수 있는지 알고 싶습니다.)
Mehrdad

tac을 사용하여 흐름을 역전시키는 것이 독창적이며 줄 바꿈과 같은 원하지 않는 문자를 제거하려면 tr -d '\ n'으로 파이프 할 수 있습니다.
Johan

4
파일 이름에 줄 바꿈, 백 슬래시 또는 인쇄 할 수없는 문자가 포함되어 있으면 중단됩니다. mywiki.wooledge.org/ParsingLs파일 라인을 반복하는 방법을 참조하십시오 . (및 연결된 스레드).
Gilles 'SO- 악의를 그만두십시오'12

가장 투표 된 답변이 f제 상황 에서 변수 를 깨뜨 렸습니다 . 그러나이 대답은 일반 for회선 을 대체하는 대체 기능으로 작동합니다 .
Sroo Stroobandt

2

공백으로 구분 된 문자열 목록을 반복하는 방법을 알아내는 사람은 다음과 같습니다.

reverse() {
  tac <(echo "$@" | tr ' ' '\n') | tr '\n' ' '
}

list="a bb ccc"

for i in `reverse $list`; do
  echo "$i"
done
> ccc
> bb 
> a

2
내 시스템에는 전술이 없습니다. 강력한 지 모르겠지만 $ {mylist}에서 x를 사용하고 있습니다. revv = "$ {x} $ {revv}"; 완료
arp

@arp tacGNU coreutils의 일부인 것처럼 충격적 입니다. 그러나 귀하의 솔루션도 좋습니다.
ACK_stoverflow

@ACK_stoverflow Linux 사용자 도구가없는 시스템이 있습니다.
Kusalananda

@Kusalananda 리눅스 만 GNU coreutils를 사용한다고 말하는가? LOL. busybox도 있습니다 tac. tac여기 에 적은 응답 의 수에서 OSX에 tac이없는 것 같습니다. 어떤 경우에는 @arp 솔루션의 변형을 사용하십시오-어쨌든 이해하기가 더 쉽습니다.
ACK_stoverflow 3

@ACK_stoverflow 이것이 내가 말하는 것입니다. 예.
Kusalananda

1
find /var/logs/ -name 'foo*.log' -print0 | tail -r | xargs -0 bar

원하는 방식으로 작동해야합니다 (Mac OS X에서 테스트되었으며 아래에주의 사항이 있습니다 ...).

man 페이지에서 찾기 :

-print0
         This primary always evaluates to true.  It prints the pathname of the current file to standard output, followed by an ASCII NUL character (charac-
         ter code 0).

기본적으로 문자열 + glob와 일치하는 파일을 찾고 NUL 문자로 종료합니다. 파일 이름에 줄 바꿈이나 다른 이상한 문자가 포함되어 있으면 find에서 잘 처리해야합니다.

tail -r

파이프를 통해 표준 입력을 가져 와서 반전시킵니다 ( 자세한 내용 은 표준 기본값 인 마지막 10 줄뿐만 아니라 모든 입력을 stdout에 tail -r인쇄 합니다man tail ).

그런 다음 파이프를 다음에 파이프합니다 xargs -0.

-0      Change xargs to expect NUL (``\0'') characters as separators, instead of spaces and newlines.  This is expected to be used in concert with the
         -print0 function in find(1).

여기서, 인수 당신이에서 전달 된 NUL 문자로 구분하여 볼 것으로 기대를 xargs 명령 find과 함께 반전 tail.

내주의 사항 : null 종료 문자열로 잘 작동하지 않는다는 것을 읽었습니다tail . 이것은 Mac OS X에서 잘 작동했지만 모든 * nix에 해당되는 것은 아닙니다. 조심스럽게 밟으십시오.

또한 GNU Parallel 이 종종 xargs대안으로 사용 된다는 점도 언급해야합니다 . 당신도 그것을 확인할 수 있습니다.

뭔가 빠졌을 수 있으므로 다른 사람들이 차임해야합니다.


2
좋은 답변 +1. 우분투가 지원하지 않는 것 같습니다 tail -r... 내가 잘못하고 있습니까?
Mehrdad

1
아니, 난 당신이 생각하지 않습니다. 리눅스 머신은 없지만 'linux tail man'에 대한 빠른 구글은 옵션으로 표시하지 않습니다. sunaku tac가 대안으로 언급 되었으므로 대신 시도해보십시오
tcdyl

또한 다른 대안을 포함하도록 답변을 편집했습니다
tcdyl

내가 +1 말했을 때 으악 +1 잊어 버렸습니다 : (완료! 그 하하에 대해 미안 :)
Mehrdad

4
tail -rOSX에만 적용되며 널로 구분 된 입력이 아니라 개행으로 구분 된 입력을 되돌립니다. 두 번째 솔루션은 전혀 작동하지 않습니다 (입력을 파이핑 ls하는 중입니다). 안정적으로 작동하게하는 쉬운 수정은 없습니다.
Gilles 'SO- 악의를 그만두십시오'12

1

귀하의 예에서 여러 파일을 반복하고 있지만 배열을 반복하거나 순서를 거꾸로 뒤집을 수있는 일반적인 제목으로 인해이 질문을 발견했습니다.

Zsh에서이를 수행하는 방법은 다음과 같습니다.

배열의 요소를 반복하는 경우이 구문을 사용하십시오 ( source )

for f in ${(Oa)your_array}; do
    ...
done

O다음 플래그에 지정된 순서를 반대로 바꿉니다. a일반적인 배열 순서입니다.

@Gilles가 말했듯이 예를 들어와 같이 globbed On파일을 역순으로 정렬합니다 my/file/glob/*(On). 때문 On이다 "역 이름 순서."

Zsh 정렬 플래그 :

  • a 배열 순서
  • L 파일 길이
  • l 링크 수
  • m 수정일
  • n 이름
  • ^o역순 ( o정상 순서)
  • O 역순으로

예를 들어 https://github.com/grml/zsh-lovers/blob/master/zsh-lovers.1.txthttp://reasoniamhere.com/2014/01/11/outrageously-useful-tips- to-master-your-z-shell /


0

Mac OSX는 tac명령을 지원하지 않습니다 . @tcdyl의 솔루션은 for루프 에서 단일 명령을 호출 할 때 작동 합니다. 다른 모든 경우에는 다음이 가장 간단한 방법입니다.

이 방법은 파일 이름에 줄 바꿈을 지원하지 않습니다. 근본적인 이유는 tail -r입력을 줄 바꿈으로 구분하여 정렬하는 것입니다.

for i in `ls -1 [filename pattern] | tail -r`; do [commands here]; done

그러나 줄 바꿈 제한을 극복하는 방법이 있습니다. 파일 이름에 특정 문자가 포함되어 있지 않다면 (예 : '=') tr모든 줄 바꿈을 사용 하여이 문자가 된 다음 정렬 할 수 있습니다. 결과는 다음과 같습니다.

for i in `find [directory] -name '[filename]' -print0 | tr '\n' '=' | tr '\0' '\n'
| tail -r | tr '\n' '\0' | tr '=' '\n' | xargs -0`; do [commands]; done

참고 :의 버전에 따라 문자로 tr지원하지 않을 수 있습니다 '\0'. 이것은 일반적으로 로케일을 C로 변경하여 해결할 수 있습니다 (그러나 일단 수정 한 후에는 내 컴퓨터에서 작동하기 때문에 얼마나 정확한지 기억하지 못합니다). 오류 메시지가 표시되고 해결 방법을 찾을 수없는 경우 의견으로 게시하여 문제 해결에 도움을 줄 수 있습니다.


0

ls -r@David Schwartz가 언급 한 것처럼 쉬운 방법을 사용 하고 있습니다.

for f in $(ls -r /var/logs/foo*.log); do
    bar "$f"
done

-4

이 시도:

for f in /var/logs/foo*.log; do
bar "$f"
done

가장 간단한 방법이라고 생각합니다.


3
질문은 " for반복 순서로 루프 "를 요구했습니다 .
manatwork
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.