글쎄, 나는 Bash에서 (기본적으로 "lastpipe" bash 옵션이 활성화되지 않은 상태에서) 파이프 이후에 할당된 모든 변수가 실제로 서브셸에서 실행되고 변수 자체가 서브셸이 실행된 후에 사라진다는 것을 알고 있습니다. 상위 프로세스에서는 더 이상 사용할 수 없습니다. 하지만 몇 가지 테스트를 해보니 다음과 같은 동작이 발견되었습니다.
A) 두 번째 명령(a=2)은 값을 할당하고 다음을 반환합니다.
[root@centos01]# a=1; a=2; a=10 | echo $a
2
B) 세 번째 명령(a=10)은 값을 할당하고 다음을 반환합니다.
[root@centos01]# a=1; a=2; a=10; a=20 | echo $a
10
C) 네 번째 명령(a=20)은 다음을 할당하고 반환합니다.
[root@centos01]# a=1; a=2; a=10; a=20; touch fileA.txt | echo $a
20
그래서:
명령 시퀀스의 마지막 변수 할당이 실제로 실행되지 않는 이유는 무엇입니까? (그렇다면 왜 서브쉘에 포착되어 echo 명령에 의해 반환되지 않습니까?)
테스트 C에서 "touch" 명령은 실제로 디렉터리에 "fileA.txt" 파일을 생성합니다. 그러면 A단계와 B단계에서 수행된 변수 할당 순서의 마지막 명령이 작동하지 않는 이유는 무엇입니까? 이에 대한 기술적 설명을 아는 사람이 있습니까?
답변1
먼저 일부 이름에 동의하기 위해 쉘이 입력을 해석하는 방법은 다음과 같습니다.
$ a=1; a=2; a=10 | echo $a
^^^ ^^^ ^^^^^^^^^^^^^^
\ \ \_ Pipeline
\ \_ Simple command
\_ Simple command
파이프라인은 두 가지 간단한 명령으로 구성됩니다.
$ a=10 | echo $a
^^^^ ^^^^^^^
\ \_ Simple command
\_ Simple command
(Bash 매뉴얼에는 명시적으로 언급되어 있지 않을 수도 있지만,POSIX 쉘 구문변수 할당만으로 간단한 명령을 구성할 수 있습니다.
a=1;
a=2;
파이프라인의 일부가 아닙니다 . A는 ;
파이프의 일부로 존재하지 않는 한 파이프를 종료합니다.복합 명령. 예를 들어:
{ a=1; a=2; a=10; } | echo $a
귀하의 예에서 다음을 실행하십시오 a=10
.echo $a
두 개의 다른, 독립 서브쉘 환경 1 은 모두 기본 환경의 복사본으로 생성됩니다. 하위 쉘은 상위 실행 환경을 변경해서는 안 됩니다2 . 관련 내용 인용POSIX 부분:
서브쉘 환경은 쉘 환경의 복사본으로 생성되어야 합니다. [...] 서브쉘 환경에 대한 변경 사항은 쉘 환경에 영향을 주어서는 안 됩니다.
그리고
또한 다중 명령 파이프라인의 각 명령은 하위 셸 환경에 있지만 확장으로 파이프라인의 일부 또는 모든 명령을 현재 환경에서 실행할 수 있습니다. 다른 모든 명령은 현재 쉘 환경 내에서 실행되어야 합니다.
따라서 예제의 모든 명령이 실제로 실행되는 동안 파이프라인 왼쪽 부분의 할당은 눈에 띄는 효과가 없습니다. a
해당 서브셸 환경의 복사본만 변경하며, 해당 복사본은 서브셸이 종료되면 손실됩니다.
파이프의 양쪽 끝에 있는 하위 쉘이 서로 직접 상호 작용할 수 있는 유일한 방법은 파이프 자체를 통하는 것입니다. 왼쪽의 표준 출력은 오른쪽의 표준 입력에 연결됩니다. a=10
파이프를 통해 아무 것도 전송되지 않으므로 에 영향을 미칠 가능성은 없습니다 echo $a
.
1 이 옵션이 설정 되면 (기본적으로 꺼짐, 내장 명령을 사용하여 활성화됨) Bash는 현재 셸의 파이프라인에서 마지막 명령을 실행할 수 있습니다. 바라보다lastpipe
shopt
관로Bash 매뉴얼에 있습니다. 그러나 이것은 귀하의 질문과 관련이 없습니다.
2 실제/역사적 관점에서 U&L에 대한 자세한 내용을 확인할 수 있습니다.이 답변도착하다POSIX 셸 스크립트 파이프라인에서 실행된 마지막 함수가 변수 값을 유지하지 않는 이유는 무엇입니까?
답변2
제 영어를 양해해 주세요. 저는 아직 배우는 중입니다. 저도 Bash 초보자이므로 답변의 실수를 바로잡아주세요. 감사합니다.
먼저 오류를 지적하고 싶습니다.
파이프를 통해 명령을 에코할 때
a=10 | echo $a
(파이프 연산자; 사용|
)a=10
파이프는stdout
명령을stdin
command2, 즉command | command2
.a=10
stdout
변수 할당입니다. 명령이 아니기 때문에 변수 할당이 없다고 가정합니다 . 변수 할당 내에서 명령 대체를 수행하면 작동하지 않습니다. 다음을 시도하면 :user@host$ a=$(b=10); echo $a
echo $a
값을 반환 하지 않습니다10
. 내가 그것을 수정하면user@host$ a=$(b=10; echo $b)
전화
$ echo $a
뒤쪽에
10
. 따라서 변수 할당이 명령이 아니라고 가정하는 것이 맞을 수도 있습니다(bash를 통해 수동으로 정의하더라도 명령이 아닙니다).
둘째, echo
명령은 에서 입력을 받지 않지만 stdin
해당 인수를 인쇄합니다.
user@host$ echo "I love linux" | echo
아무것도 반환되지 않습니다. xargs
이 문제를 해결하려면 다음 명령을 사용할 수 있습니다 .
user@host$ echo "I love linux" | xargs echo
반환됩니다 I love linux
. 따라서 파이프는 echo
대신 인수를 인쇄하기 때문에 명령에서 직접 작동 하지 않습니다 stdin
.
이제 테스트를 해보자
당신의 명령에 따라
user@host$ a=1; a=2; a=10 | echo $a
변수에는 처음에
a
값이 할당된 다음 현재 쉘 환경 내에서1
변수 값이 로 변경됩니다 .2
명령은 일반적으로 서브셸에서 실행됩니다.a=10 | echo $a
목록입니다. 즉, 서브셸에서 실행하는 것과 동일 하지만 인수를 취하지 않고 인쇄만 하기(a=10 | echo $a)
때문에 작동하지 않습니다 . 여기서 매개변수는 이고, 서브쉘의 변수값은 입니다 .echo
stdin
$a
a
2
또한
a=10
변수 할당이므로 출력이 생성되지 않습니다. 그래서 실제로는 인수로부터 아무것도 얻지 않고echo $a
인수의 값을 인쇄합니다 . 따라서 여기서는 파이프 연산자를 사용하면 안 됩니다. 대신 종결자(세미콜론)를 사용하여 명령 이름과 변수 할당을 구분하면 에서와 같이 제대로 작동합니다 .2
a=10 | < ... >
(a=10; echo $a)
이를 더 잘 이해하려면 bash 디버깅 옵션을 활성화하여 다음 예제를 시도해 볼 수 있습니다.
user@host$ a=1; a=2; a=10; echo $a;
위 의 명령줄에서
echo $a
.10
내가 그것을로 바꾸면
user@host$ a=1; a=2; (a=10; echo $a)
첫 번째와 두 번째 변수 할당은 현재 쉘에서 설정되고, 세 번째 변수 할당과
echo
명령은 서브쉘에서 실행됩니다. 따라서 값은 명령을a
실행하는10
하위 쉘에도 있으며echo
반환됩니다10
. 프롬프트를 받은 후 명령을 실행하면 하위 쉘의 변수 할당이 상위 쉘을 다시 가져오지 않기 때문에echo $a
명령이 반환됩니다 .2
- 또한 주목해야 할 점은
;
명령 구분 기호가 명령을 순차적으로 실행한다는 것입니다.
테스트 케이스 "A"와 "B"에서는 실제로 마지막 변수 할당이 수행되지만( a=10
테스트 A 및 a=20
테스트 B에서는) 명령 이후에 수행되므로 쉘 환경 변수 echo $a
의 이전 값에 대한 결과를 얻습니다 . a
그런 다음 최종 변수 할당을 수행합니다. 파이프에서는 명령이 실행되기 전에 두 명령의 stdin
합 이 연결되며 변수 할당은 표준 출력에 아무 것도 생성하지 않습니다.stdout
너무 길어요.: 파이프라인에서는 변수 할당을 사용하면 안 됩니다. echo
파이프라인에서 직접 작업하지 않습니다.
답변3
여기,
a=20 | echo $a
파이프는 혼란을 가중시킬 뿐입니다. 왼쪽의 할당은 표준 출력으로 아무것도 인쇄하지 않으며 echo
표준 입력에서 아무것도 읽지 않으므로 데이터가 파이프를 통해 이동되지 않습니다. 매개변수는 echo
이전에 설정된 것에서 단순히 확장됩니다.
말씀하신 대로 파이프라인의 다양한 부분은 서로 다른 하위 셸에서 실행되므로 왼쪽 할당은 a
오른쪽 확장에 영향을 주지 않으며 이 통신은 그 반대 방향에서도 발생하지 않습니다.
대신 다음과 같이 하면 됩니다.
{ a=999; echo a=$a; } | cat
튜브가 더 의미가 있고 로프가 튜브 a=999
를 통과할 것입니다.
마지막 예제의 코드는 touch fileA.txt
셸 외부 시스템에 영향을 미치기 때문에 작동합니다. 마찬가지로 파이프의 명령에서 stderr에 쓰고 터미널에 표시되는 결과를 볼 수 있습니다.
$ echo a >&2 | echo b >&2 | echo c >&2
b
c
a
(이것은 실제로 내 시스템에서 Bash를 사용하여 얻는 출력 순서입니다.)