$x
아래 코드 조각과 다른 값을 얻는 이유는 무엇입니까 ?
#!/bin/bash
x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x
# x=55 .. I'd expect this result
x=1
cat junk | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
x=1
echo fred | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
답변1
올바른 설명이 제공되었습니다.제스 빌링스그리고긱 드래곤, 하지만 좀 더 자세히 설명하겠습니다.
대부분의 셸(Bash 포함)에서 파이프의 각 끝은 하위 셸에서 실행되므로 셸의 내부 상태(변수 설정 등)에 대한 모든 변경 사항은 여전히 파이프의 해당 세그먼트로 제한됩니다. 서브셸에서 얻을 수 있는 유일한 정보는 출력 내용(표준 출력 및 기타 파일 설명자에 대한)과 종료 코드(0에서 255 사이의 숫자)입니다. 예를 들어 다음 코드 조각은 0을 인쇄합니다.
a=0; a=1 | a=2; echo $a
ksh(pdksh/mksh 변형이 아닌 AT&T 코드에서 파생된 변형) 및 zsh에서 파이프라인의 마지막 항목은 상위 셸에서 실행됩니다. (POSIX에서는 두 가지 동작을 모두 허용합니다.) 따라서 위의 코드 조각은 2를 인쇄합니다. 옵션을 설정하여 최신 bash에서 이 동작을 얻을 수 있습니다 lastpipe
( shopt -s lastpipe
대화형 쉘에서는 비활성화 작업 제어도 사용해야 함 set +m
).
유용한 관용구는 파이프(또는 파이프의 오른쪽에 있는 모든 항목)에 while 루프의 연속을 포함하는 것입니다. 그러나 여기서는 while 루프가 실제로 매우 일반적입니다.
cat junk | {
while read var ; do x=55 ; done
echo x=$x
}
답변2
가변 범위 문제가 있습니다. 파이프 오른쪽의 while 루프에 정의된 변수에는 자체 로컬 범위 컨텍스트가 있으며 변수에 대한 변경 사항은 루프 외부에 표시되지 않습니다. while 루프는 본질적으로 다음을 얻는 하위 쉘입니다.복사쉘 환경에 대한 모든 변경 사항은 쉘이 종료되면 손실됩니다. 이것 좀 봐StackOverflow 질문.
고쳐 쓰다: while 루프와 자체 하위 쉘이 파이프의 끝점이기 때문에 중요한 사실을 간과하여 답변에서 이를 업데이트했습니다.
답변3
~처럼다른 답변에서 언급됨, 파이프라인의 다양한 부분이 하위 셸에서 실행되므로 하위 셸에서 수정한 내용이 기본 셸에 표시되지 않습니다.
Bash만 고려한다면 구조체 외에 두 가지 다른 솔루션이 있습니다 cmd | { stuff; more stuff; }
.
입력 리디렉션프로세스 교체:
while read var ; do x=55 ; done < <(echo fred) echo "$x"
의 명령 출력은
<(...)
명명된 파이프처럼 보입니다.이
lastpipe
옵션을 사용하면 Bash가 ksh처럼 작동하고 기본 셸 프로세스에서 파이프라인의 마지막 부분을 실행하게 됩니다. 작업 제어가 비활성화된 경우에만 작동하지만, 즉 대화형 셸에서는 작동하지 않습니다.bash -c ' shopt -s lastpipe echo fred | while read var ; do x=55 ; done; echo "$x" '
또는
bash -O lastpipe -c ' echo fred | while read var ; do x=55 ; done; echo "$x" '
물론 ksh와 zsh도 프로세스 교체를 지원합니다. 그러나 어쨌든 메인 셸에서 파이프라인의 마지막 부분을 실행하게 되므로 이를 해결 방법으로 사용할 필요는 없습니다.