질문자 :eschercycle
범위가 변수로 제공될 때 Bash에서 숫자 범위를 어떻게 반복합니까?
나는 이것을 할 수 있다는 것을 알고 있습니다 ( Bash 문서 에서 "시퀀스 표현식"이라고 함).
for i in {1..5}; do echo $i; done
다음을 제공합니다.
1
2
삼
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
Jiaaroseq
방법이 가장 간단하지만 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)
과 동일하게 작동하고 다른 ((expr))
경우와 마찬가지로 Bash는 이를 산술로 취급합니다.
ephemient논의
Jiaaro가 제안한 대로 seq
사용하는 것이 좋습니다. Pax Diablo는 하위 프로세스 호출을 피하기 위해 Bash 루프를 제안했으며 $END가 너무 큰 경우 메모리 친화적인 추가 이점이 있습니다. Zathrus는 루프 구현에서 일반적인 버그를 발견했으며 i
가 텍스트 변수이기 때문에 숫자를 왔다갔다하는 연속적인 변환이 관련 속도 저하와 함께 수행된다고 암시했습니다.
정수 산술
이것은 Bash 루프의 개선된 버전입니다.
typeset -ii END let END=5 i=1 while ((i<=END)); do echo $i … let i++ done
우리가 원하는 것이 echo
echo $((i++))
쓸 수 있습니다.
ephemient 는 나에게 무언가를 가르쳐 주었다. Bash는 for ((expr;expr;expr))
구문을 허용합니다. 나는 Bash에 대한 전체 매뉴얼 페이지를 읽은 적이 없기 때문에(Korn 셸( ksh
) 매뉴얼 페이지와 마찬가지로 오래전에 읽었습니다) 그것을 놓쳤습니다.
그래서,
typeset -ii END # Let's be explicit for ((i=1;i<=END;++i)); do echo $i; done
메모리를 가장 효율적인 방법이 될 것 같다 (이 소비하는 메모리를 할당 할 필요가 없습니다 seq
하지만 아마하지 "빠른", END가 매우 큰 경우 문제가 될 수의 출력을).
초기 질문
eschercycle은 { a .. b } Bash 표기법이 리터럴에서만 작동한다고 언급했습니다. Bash 매뉴얼에 따르면 사실입니다. exec()
() 없이 fork()
이 장애물을 극복할 수 있습니다 seq
를 호출하는 경우 fork+exec가 필요함).
for i in $(eval echo "{1..$END}"); do
eval
과 echo
는 모두 Bash 내장이지만 명령 대체( $(…)
구문) fork()
tzot원래 표현이 작동하지 않는 이유는 다음과 같습니다.
man bash에서 :
중괄호 확장은 다른 확장보다 먼저 수행되며 다른 확장에 대한 특수 문자는 결과에 보존됩니다. 엄격하게 텍스트입니다. Bash는 확장의 컨텍스트나 중괄호 사이의 텍스트에 구문적 해석을 적용하지 않습니다.
따라서 중괄호 확장 은 매개변수 확장 이전에 순수한 텍스트 매크로 작업으로 초기에 수행된 것입니다.
셸은 매크로 프로세서와 보다 공식적인 프로그래밍 언어 간의 고도로 최적화된 하이브리드입니다. 일반적인 사용 사례를 최적화하기 위해 언어가 다소 복잡해지고 몇 가지 제한 사항이 허용됩니다.
추천
Posix 1 기능을 고수하는 것이 좋습니다. for i in <list>; do
사용하는 것을 의미합니다. 목록이 이미 알려진 경우 for i in <list>; do
, 그렇지 않으면 다음과 같이 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
1. Bash는 훌륭한 쉘이고 대화식으로 사용하지만 bash-ism을 스크립트에 넣지 않습니다. 스크립트에는 더 빠른 셸, 더 안전한 셸, 더 포함된 스타일의 셸이 필요할 수 있습니다. /bin/sh로 설치된 모든 항목에서 실행해야 할 수 있으며 모든 일반적인 표준에 대한 인수가 있습니다. 쉘쇼크, 일명 bashdoor를 기억하십니까? DigitalRossPOSIX 방식
이식성에 관심이 있다면 POSIX 표준 의 예를 사용하십시오.
i=2 end=5 while [ $i -le $end ]; do echo $i i=$(($i+1)) done
산출:
2 3 4 5
POSIX가 아닌 것들:
Ciro Santilli 新疆再教育营六四事件法轮功郝海东간접 참조의 또 다른 계층:
for i in $(eval echo {1..$END}); do ∶
bobbogo당신이 사용할 수있는
for i in $(seq $END); do echo $i; done
Peter Hoffmann접두사가 필요한 경우 다음과 같이 할 수 있습니다.
for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done
그것은 낳을 것이다
07 08 09 10 11 12
hossbearBSD/OS X를 사용하는 경우 seq 대신 jot를 사용할 수 있습니다.
for i in $(jot $END); do echo $i; done
jefeveizen여기에 몇 가지 아이디어를 결합하고 성능을 측정했습니다.
TL;DR 요점:
-
seq
및 {..}
정말 빠릅니다 -
for
및 while
루프가 느립니다. -
$( )
가 느립니다. -
for (( ; ; ))
루프는 더 느립니다. -
$(( ))
는 훨씬 느립니다. - 메모리의 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
Bruno Bronoskybash
에서 잘 작동합니다.
END=5 i=1 ; while [[ $i -le $END ]] ; do echo $i ((i = i + 1)) done
paxdiablo나는 이 질문이 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}
Adrian Frühwirth이것은 또 다른 방법입니다:
end=5 for i in $(bash -c "echo {1..${end}}"); do echo $i; done
Jahid중괄호 식 구문에 최대한 가깝게 유지하려면 bash-tricks의 range.bash
range
함수를 사용해 보십시오.
예를 들어 다음은 모두 echo {1..10}
과 똑같은 일을 합니다.
source range.bash one=1 ten=10 range {$one..$ten} range $one $ten range {1..$ten} range {1..10}
그것은 몇 가지 "개는"가능한 한 함께 기본 bash는 구문을 지원하려고합니다뿐만 아니라 변수가 지원되지만 유효 범위의 자주 바람직하지 않은 행동 예를 들어 문자열 (로 공급되는 for i in {1..a}; do echo $i; done
)도 방지됩니다.
다른 답변은 대부분의 경우 작동하지만 모두 다음과 같은 단점 중 하나 이상이 있습니다.
- 그들 중 다수는 성능을 저하 시킬 수 있고 일부 시스템에서는 불가능할 수 있는 서브쉘을 사용합니다.
- 그들 중 많은 사람들이 외부 프로그램에 의존합니다.
seq
조차도 사용하려면 설치해야 하고, bash에 의해 로드되어야 하고, 이 경우에 작동하기 위해 예상하는 프로그램을 포함해야 하는 바이너리입니다. 유비쿼터스 여부에 관계없이 Bash 언어 자체보다 훨씬 더 많이 의존합니다. - @ephemient와 같은 기본 Bash 기능만 사용하는 솔루션은
{a..z}
와 같은 알파벳 범위에서 작동하지 않습니다. 버팀대 확장 것입니다. 질문은 숫자의 범위에 관한 것이었기 때문에 이것은 말장난입니다. -
{1..10}
중괄호 확장 범위 구문과 시각적으로 유사하지 않으므로 둘 다 사용하는 프로그램은 읽기가 조금 더 어려울 수 있습니다. - @bobbogo의 대답은 익숙한 구문 중 일부를 사용하지만
$END
변수가 범위의 반대쪽에 대해 유효한 범위 "bookend"가 아닌 경우 예기치 않은 작업을 수행합니다. END=a
이면 오류가 발생하지 않고 {1..a}
가 표시됩니다. 이것은 Bash의 기본 동작이기도 합니다. 이는 종종 예상치 못한 일입니다.
면책 조항: 저는 링크된 코드의 작성자입니다.
Zac B이것들은 모두 훌륭하지만 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의 두 번째 인스턴스여야 하므로 집중적인 작업에 적합해야 합니다.
SuperBob{}
를 (( ))
바꿉니다.
tmpstart=0; tmpend=4; for (( i=$tmpstart; i<=$tmpend; i++ )) ; do echo $i ; done
수익률:
0 1 2 3 4
BashTheKeyboard쉘 명령을 수행하고 있고 (나처럼) 파이프라이닝에 대한 페티쉬가 있는 경우 다음이 좋습니다.
seq 1 $END | xargs -I {} echo {}
Alex Spangher이 작업을 수행하는 방법에는 여러 가지가 있지만 내가 선호하는 방법은 아래에 나와 있습니다.
seq
사용
man seq
시놉시스
$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last
통사론
전체 명령
seq first incr last
- 첫 번째는 시퀀스의 시작 번호입니다. [선택 사항, 기본적으로:1]
- incr은 증분 [선택 사항, 기본적으로: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
증분 사용
$ echo {1..10..2} 1 3 5 7 9
아래와 같은 문자에도 사용할 수 있습니다.
$ echo {a..z} abcdefghijklmnopqrstu vwxyz
theBuzzyCoderseq
' 또는 ' 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
추신: 내 bash에는 seq
' 명령이 없습니다.
Mac OSX 10.6.8, Bash 3.2.48에서 테스트됨
Zimba이것은 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 sev 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" }
Ethan Post출처 : http:www.stackoverflow.com/questions/169511/how-do-i-iterate-over-a-range-of-numbers-defined-by-variables-in-bash