programing

Bash에서 변수로 정의된 숫자 범위에서 반복하는 방법은 무엇입니까?

closeapi 2023. 5. 16. 22:39
반응형

Bash에서 변수로 정의된 숫자 범위에서 반복하는 방법은 무엇입니까?

범위가 변수에 의해 지정된 경우 Bash에서 숫자 범위를 반복하는 방법은 무엇입니까?

다음을 수행할 수 있습니다(Bash 설명서의 "시퀀스 식"이라고 함).

 for i in {1..5}; do echo $i; done

이는 다음을 제공합니다.

1
2
3
4
5

그러나 범위 끝점 중 하나를 변수로 대체하려면 어떻게 해야 합니까?이것은 작동하지 않습니다.

END=5
for i in {1..$END}; do echo $i; done

인쇄 대상:

{1..5}

for i in $(seq 1 $END); do echo $i; done

저는 편집: 선니다합을 합니다.seq할 수 보다 ;)제실로기억수있때다문방다보법들른에기할) ;)

seq방법이 가장 간단하지만 Bash에는 산술 평가 기능이 내장되어 있습니다.

END=5
for ((i=1;i<=END;i++)); do
    echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines

for ((expr1;expr2;expr3));와 같은 작품을 제작합니다.for (expr1;expr2;expr3) C와 들, 그고른처리사유▁in럼,▁other.((expr))케이스, Bash는 그것들을 산술로 취급합니다.

논의

용사를 합니다.seq지아로가 제안한 대로 괜찮습니다.Pax Diablo는 하위 프로세스 호출을 피하기 위해 Bash 루프를 제안했으며 $END가 너무 크면 메모리에 더 친숙하다는 추가적인 이점이 있습니다.Zathrus는 루프 구현에서 전형적인 버그를 발견했고, 또한 그 이후로 그것을 암시했습니다.i텍스트 변수이며, 연결된 속도 저하와 함께 연속적으로 숫자로 변환됩니다.

정수 산술

다음은 Bash 루프의 개선된 버전입니다.

typeset -i i END
let END=5 i=1
while ((i<=END)); do
    echo $i
    …
    let i++
done

우리가 원하는 것이 오직echo그러면 우리는 글을 쓸 수 있었습니다.echo $((i++)).

임시변통은 나에게 무언가를 가르쳐주었습니다: Bash는 허용합니다.for ((expr;expr;expr))ㅠㅠㅠㅠ Bash를 위한 이 없기 에 (Korn shell읽은 처럼).kshpage, 전의입니다), 저는 그것을 man page, 그고그오전일다니입놓다니저쳤습그는것을.

그렇게,

typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done

방법인 것 를 사용하기 는 없습니다seqEND가 매우 큰 경우 문제가 될 수 있는 의 출력), "가장 빠른" 것은 아닐 수도 있습니다.

첫 번째 문제

에셔사이클은 {a}에 주목했습니다.b} Bash 표기법은 리터럴에서만 사용할 수 있으며, Bash 설명서에 따르면 true입니다.하나의 (내부)로 이 장애물을 극복할 수 있습니다.fork()exec()로)seq다른 이미지인 경우에는 포크+exec)가 필요합니다.

for i in $(eval echo "{1..$END}"); do

둘다요.eval그리고.echo지만, 바시는 장이지만내형는▁are장내,만지▁a형,ins이,fork()대체를 합니다.$(…)건설).

원래의 표현이 통하지 않았던 이유가 여기에 있습니다.

맨배시에서:

브레이스 확장은 다른 확장보다 먼저 수행되며 다른 확장에 특수한 문자는 결과에 보존됩니다.그것은 엄격히 텍스트입니다.Bash는 확장 컨텍스트 또는 대괄호 사이의 텍스트에 구문 해석을 적용하지 않습니다.

따라서 브레이스 확장은 매개 변수 확장 에 순수 텍스트 매크로 작업으로 초기에 수행된 작업입니다.

셸은 매크로 프로세서와 보다 공식적인 프로그래밍 언어 간에 매우 최적화된 하이브리드입니다.일반적인 사용 사례를 최적화하기 위해 언어가 다소 복잡해지고 일부 제한 사항이 허용됩니다.

권고

저는 Posix1 기능을 고수할 것을 제안합니다.즉, 다음을 사용합니다.for i in <list>; do목이이알미경려또우않경그우은렇지는진록▁if를 합니다.while또는seq다음과 같이:

#!/bin/sh

limit=4

i=1; while [ $i -le $limit ]; do
  echo $i
  i=$(($i + 1))
done
# Or -----------------------
for i in $(seq 1 $limit); do
  echo $i
done


Bash는 훌륭한 셸이고 저는 상호작용적으로 사용하지만, 저는 스크립트에 bash-isms를 넣지 않습니다.스크립트에는 더 빠른 셸, 더 안전한 셸, 더 내장된 스타일의 셸이 필요할 수 있습니다./bin/sh로 설치된 모든 항목에서 실행해야 할 수 있으며 일반적인 pro-standards 인수가 있습니다.'배시도어'라고 불리는 '쉘쇼크' 기억나세요?

POSIX 방식

휴대성에 관심이 있는 경우 POSIX 표준의 예를 사용합니다.

i=2
end=5
while [ $i -le $end ]; do
    echo $i
    i=$(($i+1))
done

출력:

2
3
4
5

POSIX가 아닌 것:

  • (( ))POSIX 자체에서 언급한 것처럼 일반적인 확장이지만 달러는 없습니다.
  • [[.[여기면 충분합니다.참고 항목:Bash에서 단일 대괄호와 이중 대괄호의 차이점은 무엇입니까?
  • for ((;;))
  • seqGNU GNU 코어유틸스)
  • {start..end}Bash 매뉴얼에 언급된 변수로는 작동할 수 없습니다.
  • let i=i+1POSIX 72. 셸 명령 언어에 해당 단어가 없습니다.let그리고 그것은 실패합니다.bash --posix4.3.42
  • 1달러i=$i+1필요할 수도 있지만, 잘 모르겠어요POSIX 7 2.6.4 산술 확장은 다음과 같이 말합니다.

    셸 변수 x에 유효한 정수 상수를 형성하는 값이 포함되어 있고, 선택적으로 선행 더하기 또는 빼기 기호를 포함하는 경우, 산술 확장 "$(x)"와 "$(x)"는 동일한 값을 반환합니다.

    하지만 문자 그대로 그것을 읽는 것이 의미하는 것은 아닙니다.$((x+1)) 이후 확장됨x+1변수가 아닙니다.

사용할 수 있습니다.

for i in $(seq $END); do echo $i; done

또 다른 간접 계층:

for i in $(eval echo {1..$END}); do
    ∶

여기서 몇 가지 아이디어를 결합하여 성능을 측정했습니다.

TL;DR 이점:

  1. seq그리고.{..} 빠르네요.
  2. for그리고.while.
  3. $( )
  4. for (( ; ; ))는 더 .
  5. $(( )) 더 느립니다.
  6. 메모리의 N개 숫자(seq 또는 {})에 대해 걱정하고 있습니다.})은(는) 어리석습니다(최소 100만 개).

이것들은 결론이 아닙니다.당신은 결론을 도출하기 위해 이것들 각각의 뒤에 있는 C 코드를 봐야 할 것입니다.이것은 우리가 코드를 반복하기 위해 이러한 메커니즘을 사용하는 방법에 관한 것입니다.대부분의 단일 작업은 대부분의 경우 문제가 되지 않는 속도에 가깝습니다.하지만 이런 메커니즘은for (( i=1; i<=1000000; i++ ))에는 시각적으로 볼 수 있는 많은 작업이 있습니다.또한 루프당 작업 수가 사용자가 얻을 수 있는 것보다 훨씬 많습니다.for i in $(seq 1 1000000)그리고 그것은 여러분에게 분명하지 않을 수도 있습니다. 그래서 이런 테스트를 하는 것이 가치 있는 이유입니다.

데모

# show that seq is fast
$ time (seq 1 1000000 | wc)
 1000000 1000000 6888894

real    0m0.227s
user    0m0.239s
sys     0m0.008s

# show that {..} is fast
$ time (echo {1..1000000} | wc)
       1 1000000 6888896

real    0m1.778s
user    0m1.735s
sys     0m0.072s

# Show that for loops (even with a : noop) are slow
$ time (for i in {1..1000000} ; do :; done | wc)
       0       0       0

real    0m3.642s
user    0m3.582s
sys 0m0.057s

# show that echo is slow
$ time (for i in {1..1000000} ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m7.480s
user    0m6.803s
sys     0m2.580s

$ time (for i in $(seq 1 1000000) ; do echo $i; done | wc)
 1000000 1000000 6888894

real    0m7.029s
user    0m6.335s
sys     0m2.666s

# show that C-style for loops are slower
$ time (for (( i=1; i<=1000000; i++ )) ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m12.391s
user    0m11.069s
sys     0m3.437s

# show that arithmetic expansion is even slower
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; i=$(($i+1)); done | wc)
 1000000 1000000 6888896

real    0m19.696s
user    0m18.017s
sys     0m3.806s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; ((i=i+1)); done | wc)
 1000000 1000000 6888896

real    0m18.629s
user    0m16.843s
sys     0m3.936s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $((i++)); done | wc)
 1000000 1000000 6888896

real    0m17.012s
user    0m15.319s
sys     0m3.906s

# even a noop is slow
$ time (i=1; e=1000000; while [ $((i++)) -le $e ]; do :; done | wc)
       0       0       0

real    0m12.679s
user    0m11.658s
sys 0m1.004s

만약 당신이 그것을 접두사가 필요하다면 당신은 이것을 좋아할 것입니다.

 for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done

그것은 양보할 것입니다.

07
08
09
10
11
12

BSD / OS X에 있는 경우 seq 대신 jot을 사용할 수 있습니다.

for i in $(jot $END); do echo $i; done

이것은 에서 잘 작동합니다.bash:

END=5
i=1 ; while [[ $i -le $END ]] ; do
    echo $i
    ((i = i + 1))
done

이를 위한 여러 가지 방법이 있지만 제가 선호하는 방법은 아래와 같습니다.

용사를 합니다.seq

man seq

$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last

구문

명령어
seq first incr last

  • 첫 번째는 시퀀스의 시작 번호입니다. [기본값은 1].
  • incr increment [선택 사항, 기본값:1]
  • last는 시퀀스의 마지막 숫자입니다.

예:

$ seq 1 2 10
1 3 5 7 9

첫 번째와 마지막만 해당:

$ seq 1 5
1 2 3 4 5

마지막만 사용:

$ seq 5
1 2 3 4 5

용사를 합니다.{first..last..incr}

여기서 첫 번째와 마지막은 필수 사항이고 incr은 선택 사항입니다.

처음과 마지막만 사용

$ echo {1..5}
1 2 3 4 5

incr 사용

$ echo {1..10..2}
1 3 5 7 9

아래와 같은 캐릭터에도 사용할 수 있습니다.

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

나는 이 질문이 다음에 관한 것이라는 것을 압니다.bash로 - 하만지 - 참로고 -ksh93보다 지능적이고 예상대로 구현:

$ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done'
1
2
3
4
5
$ ksh -c 'echo $KSH_VERSION'
Version JM 93u+ 2012-02-29

$ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done'
{1..5}

다음과 같은 방법이 방법은 다음과 같습니다.

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done

브레이스-표현 구문에 최대한 근접하고 싶다면 bash-tricks'에서 함수를 사용해 보십시오.

예를 들어, 다음과 같은 모든 작업이 수행됩니다.echo {1..10}:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

가능한 한 적은 "gotchas"로 네이티브 bash 구문을 지원하려고 합니다. 변수가 지원될 뿐만 아니라 종종 잘못된 범위가 문자열로 제공되는 바람직하지 않은 동작(예:for i in {1..a}; do echo $i; done도 방지됩니다.

다른 답변은 대부분의 경우 효과적이지만 모두 다음과 같은 단점 중 하나 이상이 있습니다.

  • 대부분은 하위 셸을 사용하므로 성능이 저하될 수 있으며 일부 시스템에서는 불가능할 수 있습니다.
  • 그들 중 다수는 외부 프로그램에 있는 프로그램에 의존합니다.심지어.seq사용하려면 설치해야 하고, bash로 로드해야 하며, 이 경우 작동하려면 예상되는 프로그램이 포함되어 있어야 합니다.어디에 있든 없든, 그것은 단지 바시 언어 그 자체 이상으로 의존해야 할 것입니다.
  • @같이 Bash은 @epheiment 같은기 Bash 용하솔루는션은다와 같은 .{a..z}확장 의지를 다지다하지만 그 질문은 숫자의 범위에 관한 것이었기 때문에, 이것은 수수께끼입니다.
  • 대부분은 시각적으로 볼 때와 유사하지 않습니다.{1..10}두 가지를 모두 사용하는 프로그램은 읽기가 조금 어려울 수 있습니다.
  • @bobbogo의 대답은 친숙한 구문의 일부를 사용하지만, 만약에$END변수가 범위의 다른 쪽에 대해 유효한 "bookend" 범위가 아닙니다.한다면END=a예를 들어, 오류가 발생하지 않으며 동사적인 값{1..a}메아리칠 것입니다.이것은 Bash의 기본 동작이기도 합니다. 종종 예상치 못한 동작입니다.

고지 사항:저는 링크된 코드의 작성자입니다.

이것들은 모두 좋지만 seq는 아마도 더 이상 사용되지 않으며 대부분의 경우 숫자 범위에서만 작동합니다.

for 루프를 큰따옴표로 묶으면 문자열을 반향할 때 시작 및 종료 변수가 참조되지 않으며 실행을 위해 문자열을 다시 BASH로 전송할 수 있습니다. $i서브셸로 전송되기 전에 평가되지 않도록 \'로 이스케이프해야 합니다.

RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash

이 출력은 변수에도 할당할 수 있습니다.

VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`

이것이 생성해야 하는 유일한 "오버헤드"는 bash의 두 번째 인스턴스여야 하므로 집중적인 작업에 적합해야 합니다.

를 바꿉니다.{}와 함께(( )):

tmpstart=0;
tmpend=4;

for (( i=$tmpstart; i<=$tmpend; i++ )) ; do 
echo $i ;
done

산출물:

0
1
2
3
4

셸 명령을 수행하고 있고 (나처럼) 파이프라이닝에 대한 페티쉬가 있는 경우 다음과 같은 것이 좋습니다.

seq 1 $END | xargs -I {} echo {}

용하기싫면으사▁' '를,seq또는 'eval또는jot또는 산술 확장 형식(예: for ((i=1;i<=END;i++))또는 다른 루프(예: while그리고 당신은 원하지 않을 것입니다.printf그리고 행복합니다.echo다음과 같은 간단한 해결 방법이 예산에 적합할 수 있습니다.

a=1; b=5; d='for i in {'$a'..'$b'}; do echo -n "$i"; done;' echo "$d" | bash

내'PS: 내에가는 '가 없습니다.seq어쨌든 명령합니다.

Mac OS X 10.6.8, Bash 3.2.48에서 테스트됨

이것은 Bash와 Korn에서 작동하며, 더 높은 숫자에서 더 낮은 숫자로 바뀔 수도 있습니다.아마도 가장 빠르거나 예쁘지는 않지만 충분히 잘 작동합니다.네거티브도 처리합니다.

function num_range {
   # Return a range of whole numbers from beginning value to ending value.
   # >>> num_range start end
   # start: Whole number to start with.
   # end: Whole number to end with.
   typeset s e v
   s=${1}
   e=${2}
   if (( ${e} >= ${s} )); then
      v=${s}
      while (( ${v} <= ${e} )); do
         echo ${v}
         ((v=v+1))
      done
   elif (( ${e} < ${s} )); then
      v=${s}
      while (( ${v} >= ${e} )); do
         echo ${v}
         ((v=v-1))
      done
   fi
}

function test_num_range {
   num_range 1 3 | egrep "1|2|3" | assert_lc 3
   num_range 1 3 | head -1 | assert_eq 1
   num_range -1 1 | head -1 | assert_eq "-1"
   num_range 3 1 | egrep "1|2|3" | assert_lc 3
   num_range 3 1 | head -1 | assert_eq 3
   num_range 1 -1 | tail -1 | assert_eq "-1"
}

언급URL : https://stackoverflow.com/questions/169511/how-do-i-iterate-over-a-range-of-numbers-defined-by-variables-in-bash

반응형