%20%EA%B5%AC%EB%AC%B8%EC%9D%84%20%ED%97%88%EC%9A%A9%ED%95%98%EB%8A%94%20%EC%9D%B4%EC%9C%A0%EB%8A%94%20%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C%3F.png)
for 루프 구문을 이해하려고 합니다.POSIX 쉘 구문 규칙:
for_clause : For name do_group
| For name sequential_sep do_group
| For name linebreak in sequential_sep do_group
| For name linebreak in wordlist sequential_sep do_group
마지막 항목(거의 기본값)과 처음 두 항목은 posix를 인용하여 의미가 있습니다.
생략됨:
in word...
다음과 동일해야 합니다.in "$@"
따라서 처음 두 개는 쉘 스크립트의 매개변수를 반복하는 것입니다.
for 루프가 for name in; do echo $name; done
. 나는 여전히 쉘 스크립트의 매개변수를 반복하는 것이라고 생각했지만 작은 스크립트를 작성하는 것은 그렇지 않은 것 같습니다. for 루프가 약간 무시된 것 같습니다. 그렇다면 세 번째 변형의 목적은 무엇입니까?
답변1
다음과 같이 정의 하면 wordlist
됩니다.
wordlist : wordlist WORD
| WORD
그래서하나 이상말하지만 for var in ...; do ...; done
루프는 반복될 수 있습니다.0 이상For name linebreak in sequential_sep do_group
, 따라서 상황을 다루어야 합니다.단어 0개.
wordlist
~로써 정의 된0개 이상의 단어:
wordlist : wordlist WORD
| /* empty */
덜 혼란스러울 수도 있습니다.
당신이 보면SUSv2, 가지다:
for_clause : For name linebreak do_group
| For name linebreak in wordlist sequential_sep do_group
wordlist : wordlist WORD
| WORD
이는 빈 목록이 허용되지 않음을 의미합니다. 따라서 SUSv3에서는 다음을 추가하여 누락을 수정한 것 같습니다.
For name linebreak in sequential_sep do_group
정의를 변경하는 대신 wordlist
.
여기서는 쉘 언어 구문에 대해 이야기하고 있으므로 어디/확장하면 빈 목록이 발생할 수 있는지에 관한 것이 아닙니다 for i in $var; do ...; done
. 이는 모두 쉘 언어의 POSIX 형식화에 있는 WORD 태그입니다.for i in $(some cmd); do ...; done
$var
$(some cmd)
$var
$(some cmd)
우리를예얘기하지만 for i in; do ...; done
실제로는 그렇지 않습니다단어in
와 ~ 사이에있는 do
.
코드가 특별히 유용하지 않다는 데 동의합니다. 명시적으로 아무것도 반복하지 않는 루프를 작성할 이유가 없습니다. 생각할 수 있는 몇 가지 이유는 다음과 같습니다.
생성된 코드:
if ...; then
list=' "foo" "bar baz" "$var" '
else
list=
fi
eval '
for i in '"$list"'; do
printf "%s\n" "$i"
done
'
또는 sh
스크립트를 생성하고 for
명시적 매개변수에 대한 루프를 구성하는 모든 것입니다.
또는 일부 코드를 주석 처리합니다.
for commented_out in; do
this code is commented out
done
이와 같은 동안 :
:||:<<'EOF'
this code is commented out and (harmless) even
if it contains invalid syntax
EOF
더 관용적 일 것입니다.
표준을 수정하게 된 변경 요청은 찾지 못했지만 주로 기존의 모든 쉘 구현이 허용하기 때문에 변경한 것 같으니 for i in; do ...; done
표준에서 금지할 필요는 없습니다.
Bourne 쉘과 Korn에서 시작하는 대부분의 구현(이것은 주목할 만한 예외입니다)처럼 이를 허용하지 않을 타당한 이유가 없음에도 불구하고 if then ...; else ...; fi
Nor if; then ...; else ...; fi
Nor 1을 허용하지 않는다는 것을 알게 될 것입니다 if ...; then;else ...; fi
(그러나 분명히 그렇습니다). 도입되었으므로 POSIX 사양 기반 쉘이 실제로 차단됩니다.if $empty; then $empty; else $empty; fi
zsh
sh
1 실제로 !
파이프의 종료 상태를 무효화하는 키워드가 없는 Bourne 쉘에서는 Bourne 쉘이 빈 섹션을 허용하지 않기 때문에 섹션에 명시적인 null 명령을 사용하여 대신 if cmd; then :; else command if cmd fails; fi
작성 해야 합니다 .if ! cmd; then command if cmd fails; fi
:
then
then
답변2
예를 들어 이러한 동작의 이유를 설명하는 것이 가장 좋습니다. 호출된 변수가 있고 ${items}
해당 변수의 각 단어에 대해 함수를 호출해야 하지만 경우에 따라 빈 문자열로 평가될 수 있다고 가정해 보겠습니다 .
POSIX 호환 동작의 경우 다음을 사용하면 됩니다.
for x in ${items} ; do
do_something(x)
done
여기서는 평가 결과가 빈 문자열인지 여부는 중요하지 않습니다 ${items}
. 빈 문자열이면 루프는 반복할 항목이 없으며 그냥 건너뛰게 되기 때문입니다.
그러나 쉘이 빈 단어 목록 대소문자를 다르게 처리하는 경우(전혀 처리하지 않거나 없는 형식 중 하나와 동일 in
) 다음이 필요합니다(적어도).
if [ -n "${items}" ]; then
for x in ${items} ; do
do_something(x)
done
fi
따라서 길이를 알 수 없는 빈 항목 목록을 반복해야 하는 비교적 일반적인 상황에서 이 동작은 (적어도) 어느 정도 들여쓰기 및 추가 조건 확인을 절약합니다. 항목 모음(예: Python 또는 JavaScript)을 반복하는 for 루프가 있는 다른 많은 언어는 정확히 동일한 방식으로 동작합니다. 루프에 제공할 변수(또는 리터럴)를 명시적으로 요청하는 반면, 이를 수행하는 쉘 스크립트는 반복할 항목의 리터럴 목록이 정의되는 방식(즉, 빈 목록은 따옴표가 없는 빈 문자열임) 때문에 존재하지 않는 것 같습니다.