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

당신이 위치 매개 변수를 사용하면, 당신의 변수에 대한 필요가 없습니다 i및 f; 다만 수행 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.txt 및 http://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.