파이프라인 이후 여러 명령 및 서브셸 실행

파이프라인 이후 여러 명령 및 서브셸 실행

좋아, 나는 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 옵션이 설정 되면 lastpipe(기본적으로 꺼져 있으며 shopt내장을 사용하여 활성화할 수 있음) Bash는 현재 셸에서 파이프라인의 마지막 명령을 실행할 수 있습니다. 보다파이프라인Bash 매뉴얼에서. 그러나 이는 귀하의 질문과 관련이 없습니다.

2 U&L에 대한 실제적/역사적 관점에서 자세한 내용을 찾을 수 있습니다.이 답변에게POSIX 셸 스크립트 파이프라인에서 실행된 마지막 함수가 변수 값을 유지하지 않는 이유는 무엇입니까?

답변2

제 영어를 양해해 주세요. 저는 아직 영어를 배우는 중입니다. 저는 또한 Bash 초보 학습자이므로 답변에서 실수를 수정해 주시기 바랍니다. 감사합니다.

먼저 오류를 지적하겠습니다.

  • a=10 | echo $a명령을 에코하기 위해 파이프 연산자(파이프 연산자; 사용 |)를 사용하고 있습니다 a=10. 파이핑은 stdouta 명령을 stdincommand2, 즉 에 연결합니다 command | command2.

  • a=10stdout변수 할당이므로 명령이 아니기 때문에 변수 할당이 없다고 가정하겠습니다 . 변수 할당 값에서 명령 대체를 수행하면 작동하지 않습니다. 다음을 시도하면 :

    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)하위 쉘에서 실행되는 것과 동일하지만 does not echotake 처럼 작동하지 않고 stdin인수만 인쇄합니다. 여기서 인수는 하위 쉘의 $a변수 값인 입니다 .a2

  • 또한 a=10변수 할당이므로 출력이 생성되지 않습니다. 따라서 사실상 에서 아무것도 가져오지 않고 echo $a인수의 값을 인쇄하는 것입니다 . 따라서 여기서는 파이프 연산자를 사용하면 안 됩니다. 대신 명령 이름과 변수 할당을 종결자(, 세미콜론)로 분리하면 에서와 같이 제대로 작동합니다 .2a=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있으므로 를 반환합니다 . 프롬프트를 받은 후 명령을 실행하면 하위 쉘의 변수 할당이 상위 쉘로 다시 전달되지 않으므로 명령이 반환됩니다 .10echo10echo $a2

  • 한 가지 더 주목할 점은 ;명령 구분 기호가 명령을 순차적으로 실행한다는 것입니다.

테스트 케이스 "A"와 "B"에서는 마지막 변수 할당( a=10테스트 A 및 a=20테스트 B)이 실제로 실행되지만 명령 이후에 실행되므로 다음 과 같은 echo $a이전 변수 값의 결과를 얻습니다. a하위 쉘 환경, 그 후에 마지막 변수 할당이 실행됩니다. 파이프라인에서는 명령이 실행되기 전에 두 명령이 연결되며, 변수 할당도 stdout에서 아무 것도 생성하지 않습니다 stdin.stdout

tl;dr: 파이프라인에서는 변수 할당을 사용하면 안 됩니다. echo파이프라인에서 직접 작동하지 않습니다.

답변3

여기,

a=20 | echo $a

파이프는 혼란을 더할 뿐입니다. 왼쪽의 할당은 stdout에 아무것도 인쇄하지 않으며 echostdin에서 아무것도 읽지 않으므로 데이터가 파이프를 통해 이동되지 않습니다. 인수는 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를 사용하여 얻은 출력 순서입니다.)

관련 정보