쉘 변수는 어떤 범위를 가질 수 있습니까?


42

쉘 변수의 범위에 대해 명확하지 않은 문제가 발생했습니다.

의 작업을 수행하기 위해 bundle install값을 사용하는 Ruby 명령 인을 사용 하려고 $GEM_HOME했습니다. 내가 설정 $GEM_HOME했지만 명령 export은에서와 같이 사용할 때까지 해당 값을 무시 했습니다 export GEM_HOME=/some/path.

이것이 변수를 어떻게 든 "전역"( 환경 변수 라고도 함 )으로 만들지 만 그 의미를 이해하지 못합니다. 프로그래밍의 글로벌에 대해서는 알고 있지만 다른 프로그램에는 적용되지 않습니다.

또한 내 변수 설정이 현재 쉘 세션에만 적용된다는 것을 감안할 때 데몬 처리 된 프로세스에 대해 어떻게 설정합니까?

쉘 변수는 어떤 범위를 가질 수 있습니까?

답변:


33

프로세스는 트리로 구성되어 있습니다 모든 프로세스 떨어져에서 유일한 부모가 가지고 init있는 PID항상 1이고 부모가없는합니다.

새로운 프로세스의 생성은 일반적으로 한 쌍의 fork/ execv시스템 호출을 통해 이루어 지며, 여기서 자식 프로세스의 환경은 부모 프로세스 의 복사본 입니다.

쉘에서 환경 변수를 변수에 넣으려면 export모든 자식에게 재귀 적으로 볼 수 있습니다. 그러나 자식이 변수의 값을 변경하면 변경된 값은 해당 변수와 해당 변경 생성 모든 프로세스 ( 이전에 말한 것처럼 사본 )에만 표시됩니다 .

하위 프로세스가 환경을 변경할 수 있다는 것을 고려하십시오. 예를 들어, 아마도 login예를 들어 수행되는 것처럼 환경을 기본값으로 재설정 할 수 있습니다.


1
아! 좋아, 내가 이해하는지 보자. 쉘에서 내가 말하면 FOO=bar현재 쉘 프로세스의 값을 설정합니다. 그런 다음 ( bundle install) 와 같은 프로그램을 실행하면 하위 프로세스가 생성되어 액세스 할 수 없습니다 FOO. 내가 말했다하지만 export FOO=bar, 자식 프로세스 (와 그 자손)는 것입니다 그것을에 액세스 할 수 있습니다. 그들 중 하나는 차례로 export FOO=buzz그 자손 FOO=buzz의 가치를 바꾸 거나 그 자체의 가치 만 바꾸 라고 요구할 수 있습니다 . 그게 맞습니까?
Nathan Long

2
@NathanLong 정확히 그렇지는 않습니다. 모든 현대 쉘에서 변수가 내보내 지거나 값의 변경 사항이 자손 환경에 반영되거나 내 보내지 않습니다 (변수가 환경에 없음을 의미). 특히, 쉘이 시작될 때 변수가 이미 환경에 있으면 익스포트됩니다.
Gilles 'SO- 악마 그만해

2
"자식이 변수의 값을 변경하면 변경된 값은 변수와 그 변경 후에 만들어진 모든 프로세스에만 표시됩니다"라는 문장에 약간 혼란 스러웠습니다. "... 그것과 그 변경 후 생성 된 모든 하위 프로세스에 표시"라고 말하는 것이 더 정확합니다. 부모 프로세스의 다른 자식은 자식 프로세스 이후에 시작된 자식도 영향을받지 않습니다.
Jaan

26

적어도 under kshbash에서 변수는 세 가지 범위를 가질 수 있으며 나머지 모든 대답과 같이 가지 범위 가 현재는 아닙니다 .

반출 된 (즉, 환경) 변수 및 셸 반출되지 않은 변수 범위 외에도 함수 로컬 변수에 대한 세 번째 좁은 범위가 있습니다.

typeset토큰을 사용하여 쉘 함수에서 선언 된 변수는 선언 된 함수 및 호출 된 (서브) 함수에서만 볼 수 있습니다.

ksh/ bash코드 :

# Create a shell script named /tmp/show that displays the scoped variables values.    
echo 'echo [$environment] [$shell] [$local]' > /tmp/show
chmod +x /tmp/show

# Function local variable declaration
function f
{
    typeset local=three
    echo "in function":
    . /tmp/show 
}

# Global variable declaration
export environment=one

# Unexported (i.e. local) variable declaration
shell=two

# Call the function that creates a function local variable and
# display all three variable values from inside the function
f

# Display the three values from outside the function
echo "in shell":
. /tmp/show 

# Display the same values from a subshell
echo "in subshell":
/tmp/show

# Display the same values from a disconnected shell (simulated here by a clean environment start)
echo "in other shell"
env -i /tmp/show 

이 출력을 생성합니다.

in function:
[one] [two] [three]
in shell:
[one] [two] []
in subshell:
[one] [] []
in other shell
[] [] []

보시다시피, 내 보낸 변수는 처음 세 위치에서 표시되고, 내 보낸 변수는 현재 쉘 외부에 표시되지 않으며 함수 로컬 변수는 함수 자체 외부에 값이 없습니다. 마지막 테스트에서는 값이 전혀 표시되지 않습니다. 이는 내 보낸 변수가 셸간에 공유되지 않기 때문입니다. 즉, 상속 된 변수 만 상속 될 수 있으며 상속 된 값은 나중에 부모 셸의 영향을받을 수 없습니다.

이 후자의 동작은 모든 프로세스에서 완전히 전역 적으로 공유되는 시스템 변수를 사용할 수있는 Windows의 동작과는 상당히 다릅니다.


12

그들은 프로세스에 의해 범위가 지정됩니다

다른 응답자들은 쉘 변수 범위가 프로세스와 그 자손 에 관한 것임을 이해하도록 도와주었습니다 .

ls명령 행에서 와 같은 명령을 입력하면 실제로 ls프로그램 을 실행하기위한 프로세스가 실행 됩니다. 새로운 프로세스는 쉘을 부모로합니다.

모든 프로세스는 자체 "로컬"변수를 가질 수 있으며 자식 프로세스에는 전달되지 않습니다. 또한 "환경"변수를 설정할 수도 있습니다. 를 사용 export하면 환경 변수가 생성됩니다. 두 경우 모두 관련없는 프로세스 (원본의 피어)는 변수를 볼 수 없습니다. 우리는 자식 프로세스가 보는 것을 통제하고 있습니다.

우리가 A라고 부르는 bash 쉘이 있다고 가정 해 봅시다. 당신은을 호출 bash하는 하위 프로세스 bash 쉘을 작성합니다. 우리는 B라고 부릅니다 export. A에서 호출 한 것은 여전히 ​​B로 설정됩니다.

이제 B에서는이라고 말합니다 FOO=b. 다음 두 가지 중 하나가 발생합니다.

  • B가 A라는 환경 변수를받지 못하면 FOO로컬 변수를 만듭니다. B의 자녀는 B를받지 않는 한 그것을받지 못합니다 export.
  • B (A로부터) 호출 된 환경 변수를 수신 한 경우 FOO, 그 자체와 그 이후 분기 된 자식에 대해 환경 변수 를 수정합니다 . B의 자녀에게는 B가 할당 한 값이 표시됩니다. 그러나 이것은 A에 전혀 영향을 미치지 않습니다.

다음은 간단한 데모입니다.

FOO=a      # set "local" environment variable
echo $FOO  # 'a'
bash       # forks a child process for the new shell
echo $FOO  # not set
exit       # return to original shell
echo $FOO  # still 'a'

export FOO # make FOO an environment variable
bash       # fork a new "child" shell
echo $FOO  # outputs 'a'
FOO=b      # modifies environment (not local) variable
bash       # fork "grandchild" shell
echo $FOO  # outputs 'b'
exit       # back to child shell
exit       # back to original shell
echo $FOO  # outputs 'a'

이 모든 것이 내 원래 문제를 설명합니다. GEM_HOME쉘에 설정 했지만 호출 할 때 bundle install자식 프로세스가 만들어졌습니다. 내가 사용하지 않았기 때문에 export자식 프로세스는 쉘을받지 못했습니다 GEM_HOME.

수출 금지

을 사용하여 변수를 "내보내기"할 수 있습니다 (자식으로 전달되지 않도록) export -n FOO.

export FOO=a   # Set environment variable
bash           # fork a shell
echo $FOO      # outputs 'a'
export -n FOO  # remove environment var for children
bash           # fork a shell
echo $FOO      # Not set
exit           # back up a level
echo $FOO      # outputs 'a' - still a local variable

1
"자체와 그 하위 항목에 대해 수정합니다"라고 말하면 수정 작성된 하위 항목 만 수정 된 값을 보게됩니다.
enzotib 2012 년

1
@enzotib-좋은 지적. 업데이트되었습니다.
Nathan Long

3

내보내기에 대해 찾을 수있는 가장 좋은 설명은 다음과 같습니다.

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html

서브 쉘 또는 하위 쉘 내에 설정된 변수는 그것이 정의 된 서브 쉘에서만 볼 수 있습니다. 내 보낸 변수는 실제로 환경 변수로 만들어집니다. 따라서 분명히 변수를 내 보낸 경우를 제외하고는 bundle install자체 쉘을 실행하여 보이지 않습니다 .$GEM_HOMEenvironment

변수 범위에 대한 설명서를 여기서 볼 수 있습니다.

http://www.tldp.org/LDP/abs/html/subshells.html


아, 그래서 나는 "환경 변수"라는 용어를 잘못 사용했다 FOO=bar; 당신은 export하나를 만들기 위해 사용해야 합니다. 이에 따라 질문이 수정되었습니다.
Nathan Long

내가 추가 한 링크를 살펴보십시오.
Karlson

3

예상대로 가변 범위의 계층 구조가 있습니다.

환경

가장 바깥 쪽 범위는 환경입니다. 운영 체제에서 관리하는 유일한 범위이므로 모든 프로세스에 대해 존재합니다. 프로세스가 시작되면 부모 환경의 사본이 수신되고 그 후에 두 환경이 독립됩니다. 자녀의 환경을 수정해도 부모의 환경이 변경되지 않으며 부모의 환경을 수정해도 기존의 자식 환경이 변경되지 않습니다.

쉘 변수

쉘에는 변수에 대한 고유 한 개념이 있습니다. 이것은 상황이 약간 혼란되기 시작하는 곳입니다.

쉘의 변수에 값을 지정하고 해당 변수가 환경에 이미 존재하면 환경 변수가 새 값을 수신합니다. 그러나 변수가 아직 환경에 없으면 변수가됩니다. 쉘 변수는 Ruby 스크립트 내에 만 존재하는 방식과 유사하게 쉘 프로세스에만 존재합니다. 자식 프로세스에 의해 상속되지 않습니다.

export키워드가 사용되는 위치는 다음과 같습니다 . 쉘 변수를 쉘 프로세스 환경에 복사하여 자식 프로세스가 상속 할 수 있도록합니다.

지역 변수

지역 변수는 해당 변수를 포함하는 코드 블록으로 범위가 지정된 쉘 변수입니다. typeset키워드 (portable) local또는 declare(Bash)를 사용하여 지역 변수를 선언 합니다. 다른 쉘 변수와 마찬가지로 로컬 변수는 자식 프로세스에 의해 상속되지 않습니다. 또한 지역 변수는 내보낼 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.