그래서 나는 이렇게 하려고 노력한다
for x in {1..7000}
do
echo $x
done
출력은 다음과 같습니다{1..7000}
또한 C 스타일 for 루프가 mksh에서 작동하지 않는다는 것도 배웠습니다.
인터넷 검색을 좀 했지만 대부분 mksh가 아닌 ksh에 대한 정보입니다.
그렇다면 mksh에서 범위를 반복하는 올바른 방법은 무엇입니까?
노트:나는 문자열을 인쇄하는 것보다 반복과 C와 유사한 동작을 생성하는 올바른 방법에 더 관심이 있습니다.
답변1
그럼 먼저, 있는 그대로여기서 간략하게 논의했습니다일반적인 형태...
for arg in {brace..expanded..set}; do ...
...내 생각에는 이것이 쉘 프로그래밍 오류의 대표적인 예입니다. 루프에서 수식 세트를 생성하고 각 결과를 구분된 배열에 저장한 후 다음으로 전달합니다.또 다른 루프배열을 반복할 목록으로 나타냅니다.
일반적으로 대괄호 확장은 프로그래밍 환경에서 특별히 유용하지 않습니다. 왜냐하면 모든 쉘 프로그래밍은 어떤 방식으로든매크로그리고 버팀대 확장이미 매크로임. 버팀대 확장은(나도 생각한다)ㅏ굉장히 유용하다대화형 셸 기능은 매크로이기 때문에 유용합니다. 그렇지 않으면 낭비가 됩니다.
지원 확장도 이식 가능하지 않습니다.(읽기: POSIX - 휴대용). 내가 아는 한, 일반적으로 현재 버전의 zsh
, bash
, , 에서는 작동할 수 있지만 yash
다른 곳에서는 작동하지 않습니다. 이러한 셸에서도 셸 옵션을 사용하여 비활성화할 수 있으므로 일반적으로 또는 의 출력에서 가용성을 테스트할 수 있지만 ksh93
셸 기능의 맥락에서 절대적으로 의존할 수는 없습니다 . 그 사이에 위의 쉘 각각에는 서로 다른 종류의 테스트가 필요하므로 이를 함수에 사용하여 테스트하고 싶다면 대상 쉘의 매뉴얼에서 구체적인 내용을 확인하시기 바랍니다.$-
set +o
다른 셸에서는 명령 대체의 출력을 분할하여 거의 동일한 동작을 얻을 수 있습니다. 단, 컬렉션을 생성하려면 외부 실행 파일을 호출해야 한다는 점만 다릅니다 $IFS
. 이는 프로그램 seq
을 사용할 수 있는 곳에서 쉽게 시연됩니다. 예를 들어…
unset IFS
### ^ensure $IFS default behavior
for arg in $(seq 7000)
do printf %d\\n "$arg"
done
...또는...
unset IFS
printf %d\\n $(seq 7000)
...하지만 쓰여진 대로 둘 다 단점 외에는 아무것도 제공하지 않습니다...
seq 7000
그러나 첫 번째 예의 경우 방법은 중괄호 확장 버전과 거의 동일합니다. 두 경우 모두, 일부 다른 함수나 프로세스는 완전한 인수 목록을 생성한 for
다음 컨트롤 생성자에 의해 반복됩니다. 그러나 후자의 경우,(이외의 쉘의 ksh93
경우파이프 열기/해체도 있으며, 셸 프로세스와 해당 하위 프로세스를 분기 seq
하고 마지막으로 $IFS
.
$IFS
그런데 분할은 사용된 껍질에 따라 매우 다른 속도로 발생합니다. bash
예를 들어, 단순 $IFS
분할 성능은 종종 형편없지만 dash
분할 문자열의 줄 길이에 따라 중괄호 확장을 수행하는 다른 쉘만큼 빠를 수도 있습니다.
그러나 중괄호 확장을 사용하든 분할을 사용하든 관계없이 $IFS
최종 결과는 일부 루프를 실행하여 수식 목록을 생성한 다음 다른 루프를 실행하여 해당 목록을 반복하는 것입니다. 대신, 여러분이 할 수 있고 아마도 해야 할 일은 목록을 생성한 수식의 적용에 대해 루프를 실행하고, 사용된 반복자를 가능한 한 빨리 처리하고, 만족스러운 결과를 위해 각 반복을 테스트하는 것입니다. C에서는 이런 일을 할 것입니다 ...
for ( x = 0; x < 10; x++ ){ ...
이는 실제로 에서 수행할 수 있는 작업과 유사 하거나 에서 bc
사용할 수 있는 편리한 구문 형식과 매우 유사합니다.ksh93
zsh
bash
for (( x=0; x<10; x++ )); do ...
그러나 이것도 휴대용이 아닙니다.(다시 읽기: POSIX). 대신 다음을 이식 가능하게 수행할 수 있습니다.
iterator=$(( start_value - interval ))
while [ "$(( iterator += interval ))" -le "$end_value" ]; do ...
...또는...
iterator=$(( start_value - interval ))
while [ "$(( ( iterator += interval ) <= end_value ))" -ne 0 ]; do ...
POSIX 지정C 산술 연산(즉, 정수가 포함되는 경우)에 대한 쉘 산술 확장 및 기능 패리티 검사와 유사합니다. 결과적으로 단 한 번의 확장으로 복잡한 수식을 편리하고 이식성 있게 반복할 수 있으며, 여러 변수를 동시에 동시에 반복할 수도 있습니다.
반면에 이러한 구성은 다른 C 언어와 셸 언어의 불평등을 해결하기 어렵게 만들 수 있습니다. 쉘 읽기 및 쓰기는 거의 항상 리터럴 s 및 s(각 시스템 호출 자체)이며 read()
쉘 구문에는 이식 가능 및 /또는 아날로그가 write()
없습니다 . 따라서 각 반복에서 이러한 작업을 수행하는 루프는(이 작업을 수행하기 위해 내장 함수만 사용하는 경우에도) 성능 저하를 초래할 수 있습니다.fread()
fwrite()
setbuf()
그러나 때로는 쉘 스크립트에서 전체 컬렉션을 순차적으로 또는 병렬로 빌드하고 실행하는 것 사이에서 절충안을 찾을 수 있습니다.
set 0 1 2 3 4 5 6 7 8 9
for n do printf "$n%d\n" "$@"; done
이 작업은 write()
10번 반복하여 10번 수행되며 00 - 99 사이에서 개행으로 구분된 시퀀스를 인쇄합니다. 이것은 매우 작은 예이고 여전히 낭비입니다. 30바이트 write()
는 3바이트에 비해 크게 개선된 것이 아니기 때문입니다. write()
그러나 이는 여러분이 사용할 수 있는 이러한 유형의 접근 방식을 보여주는 것입니다. 앞서 말했듯이 쉘 프로그래밍 언어는 매우 유용하다.매크로내부에모든 것은 문자열이다. 따라서 대상 세트를 더 작고 관리하기 쉬운 세트로 나눈 다음 루프 컨텍스트에서 반복적으로 호출하여 궁극적으로 대상 세트를 생성할 수 있는 경우 더 성능이 좋은 결과를 얻을 수 있는 경우가 많습니다.
다음은 좀 더 복잡하고 매크로와 유사한 예입니다.
sh -c '
i=0 _i=-25
set "$1" "$1" "$1" "$1" "$1"
for n do eval "
printf %b%d $@ $@ $@ $@ $@"
done' -- \
'"\01$((1+(i<(i+=!(_i=(_i+=25)%275)))))" "$i$_i"'
write()
각각 평균 약 100바이트에 대해 5번 반복하고 다음을 인쇄합니다.
10 125 150 175 1100 1125 1150 1175 1200 1225 1250
20 225 250 275 2100 2125 2150 2175 2200 2225 2250
30 325 350 375 3100 3125 3150 3175 3200 3225 3250
40 425 450 475 4100 4125 4150 4175 4200 4225 4250
50 525 550 575 5100 5125 5150 5175 5200 5225 5250
60 625 650 675 6100 6125 6150 6175 6200 6225 6250
70 725 750 775 7100 7125 7150 7175 7200 7225 7250
80 825 850 875 8100 8125 8150 8175 8200 8225 8250
90 925 950 975 9100 9125 9150 9175 9200 9225 9250
100 1025 1050 1075 10100 10125 10150 10175 10200 10225 10250
110 1125 1150 1175 11100 11125 11150 11175 11200 11225 11250
120 1225 1250 1275