그러면 사물은 어떻게 "환경"에 들어가나요?

그러면 사물은 어떻게 "환경"에 들어가나요?

Linux(구체적으로는 Ubuntu 13.04) 환경의 동작을 이해하려고 노력하면서 환경 변수 설정이 다른 컨텍스트에서 사용되거나 정의되는 다양한 상황을 발견했습니다. 예를 들어 확인하면 다음과 같은 locale결과를 얻습니다.

$ locale
LANG=en_US.UTF-8
LANGUAGE=es_ES:es_HN:es_EC:en
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=es_ES.UTF-8
// more output

LC_CTYPE그러나 예를 들어 using 을 찾으면 env | grep "LC_CTYPE"출력이 전송되지 않습니다. 일반적으로 locale13개의 LC_*변수가 표시되지만 env9개만 표시됩니다.

$ locale | grep "LC_*" | wc -l
13
$ env | grep "LC_*" | wc -l
9

다른 "속성"을 가진 다른 변수는 입니다 PS1. 예를 들어:

$ env | grep "PS1" # No output, but...
$ set | grep "PS1" | head -n 1
PS1=$'\\[\\033[1;33m\\][\\t][\\W]\342\230\233\\[\\033[0m\\] '

물론 PS1현재 환경에서는 프롬프트가 그에 따라 변경되는 것을 볼 수 있으므로 잘 정의된 변수입니다.

다른 방법보다다른 컨텍스트의 환경 변수가 전달되며 strace, 프로그램을 실행하면 어떤 일이 일어나는지 확인할 수 있는 프로그램입니다. 견본:

$ strace -v ./a.out # a.out is a common Hello World, made in C.
execve("./a.out", ["./a.out"], ["LC_PAPER=es_ES.UTF-8", ...]) = 0
brk(0)
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
# etc, etc
write(1, "Hello World\n", 12Hello World
)           = 12
exit_group(0)                           = ?

프로그램을 실행할 때 쉘이 가장 먼저 하는 일은 call 이며 execve, 프로그램을 호출합니다. 첫 번째 매개변수는 호출된 프로그램, 두 번째 매개변수는 argv호출된 프로그램의 매개변수, 세 번째 매개변수는 환경 변수입니다.

예를 들어, 세 번째 인수에서는 PS1or does not listed 입니다 LC_TYPE.

일반적으로 에 전송되는 환경 변수 목록에 나타나 env거나 나타나는 변수입니다 . 일부 변수는 또는 에 표시되지만 다른 변수는 표시되지 않습니다( 및 및 그러나 null 값이 있음). 마지막으로, 기타 변수는 눈에 보이는 효과( )가 있지만 반영된 대로 정의되지 않습니다 .setexecvelocaleenvsetLC_TYPELC_COLLATELC_MESSAGELC_ALLenvPS1set

여기서 무슨 일이 일어나고 있는 걸까요? (매개변수 없음), (분명히 로케일 변수만 고려됨) env의 차이점은 무엇입니까?setlocale

답변1

$PS1예를 들어 보고서가 없는 이유를 설명하는 여기서 주요 문제는 보고서에서 비롯된다는 것 env입니다.env비대화형환경. 프로세스는 지점에서 실행됩니다.인터렉티브쉘이지만 환경이 설정되는 방식에는 미묘함이 있습니다. 실제로 exec()모든 프로세스에 대해 설정된 기본 C 수준 외부 변수를 통해 상속됩니다(참고자료 참조 man environ). 예는 다음과 같습니다.

#include <stdio.h>

extern char **environ;

int main (void) {
    int i;
    for (i = 0; environ[i] != NULL; i++) {
        printf("%s\n", environ[i]);
    }
    return 0;
}      

흥미롭게도 이를 컴파일하고 실행하면 찾은 내용이 보고 **environ된 내용과 정확히 일치합니다 env.

$ gcc test.c
$ ./a.out > aout.txt
$ env > env.txt
$ diff env.txt aout.txt
68c68
< _=/bin/env
---
> _=./a.out

유일한 차이점은 실행 파일의 이름입니다. 그렇다면 그것은 어디 **environ에서 왔으며 왜 포함되지 않습니까 $PS1?

기본 설명은 프로세스는 항상 다른 프로세스의 하위 프로세스로 생성되고 상속되지만 **environ프로세스 PS1의 일부는 아니라는 것입니다. 시작되면 쉘은 표준 위치에서 변수를 얻을 수 있으며 이는 쉘이 대화형인지 여부에 따라 다릅니다.부르다존재하다 man bash. 이것의 한 가지 측면은 다음과 같습니다.

PS1이 설정되었습니다 [...]Bash가 대화형이라면,쉘 스크립트 또는 시작 파일이 이 상태를 테스트하도록 허용합니다.

이제 /etc/bashrc다음과 같은 내용을 확인하세요.

# are we an interactive shell?
if [ "$PS1" ]; then

실제 (멋진) 프롬프트가 설정되는 곳이며 해당 프롬프트나 초기 값이 편집 $PS1되지 않았습니다 export. 초기 값은 대화형이므로 호출 시 셸에 의해 생성된 다음 파일을 가져오지만 PS1다음 **environ명령을 실행하면 이를 볼 수 있습니다.

#!/bin/sh

echo $PS1

없음 - echo $PS1대화형 셸에 있을 때 정의되어 있더라도 마찬가지입니다. 이는 **environ실행이 상위 대화 형 셸의 실행 과 #!/bin/sh동일하지만 PS1.**environ**environ환경 변수).

의 내용은 **environ에 있습니다 /proc/[PID]/environ. 현재 대화형 셸에서 내용을 확인하면 해당 내용이 없음 cat /proc/$BASHPID/environ을 알 수 있습니다 .PS1

그러면 사물은 어떻게 "환경"에 들어가나요?

간단한 대답은 시스템 호출을 통해서입니다. 예를 들어, 이전 예제 C 프로그램에 무언가를 넣으면 다음과 같습니다.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

int main (void) {
    int i;
    if (putenv("MYFOO=whatbar?")) {
        fprintf(stderr, "putenv() failed: %s\n", strerror(errno));
        exit(1);
    }

    for (i = 0; environ[i] != NULL; i++) {
        printf("%s\n", environ[i]);
    }

    return 0;
}           

MYFOO=whatbar?출력에 나타납니다( 참조 man putenv). 쉘은 fork()ing(상위 프로세스의 메모리 스택 복사)과 호출 execv()(복사된 을 전달 )을 통해 프로세스를 생성하므로 환경 변수를 하위 프로세스에 **environ전달하는 메커니즘을 볼 수 있습니다 .export

예제 에 를 넣으면 fork()이것이 사실임을 알 수 있으며, (반복하자면) 분기 및 잠재적 실행 프로세스는 **environ하위 프로세스가 생성되고 조상으로부터 상속되는 방식입니다. 호출은 프로세스 이미지를 대체하지만 및 exec에 따라 시스템에 의해 전달됩니다 (참고. 전자의 일부 버전에서는 이를 참조하지 않음) .man execvman environ**environ

/usr/bin/env이것은 MYFOO=whatbar?내보내기를 통한 리터럴 포크 및 실행 입니다 putenv().

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main (void) {
    pid_t pid;

    if (putenv("MYFOO=whatbar?")) {
        fprintf(stderr, "putenv() failed: %s\n", strerror(errno));
        exit(1);
    }

    pid_t pid = fork();
    if (!pid) execl("/usr/bin/env", "env", NULL);

    return 0;
}         

그렇다면 "환경" 밖의 것들은 어디에 있습니까?

특정 쉘 인스턴스에 대한 개인 데이터입니다. Bash는 인수 없이 상속된 환경 콘텐츠를 표시합니다 set. 이 출력에는 소스 기능도 포함되어 있습니다.

그러나 예를 들어 env LC_CTYPE을 사용하여 grep "LC_CTYPE"을 찾으면 출력이 전송되지 않습니다. 일반적으로 locale은 13개의 LC_* 변수를 표시하는 반면 env는 9개만 표시합니다.

LC_env(단지 ) 에서는 변수를 전혀 얻지 못했지만 LANG에서는 13개의 변수를 얻었습니다 locale. 나는 이것이 locale내보내진 것이 아니라 호출에 의해 설정된 변수라고 생각합니다. 그로부터 정보를 얻는다는 사실은 env아마도 일부 구성의 순진한 버그를 반영할 것입니다.

답변2

쉘은 두 가지 유형의 변수를 알고 있습니다.

  1. 쉘(및 서브쉘)에만 알려진 "내부" 변수

  2. execve내보낸 변수는 표시되는 "공식" 변수입니다 env. 쉘 내장에는 export내보낸 변수가 표시됩니다.

실행하면

export PS1

그리고 반복하다

env | grep "PS1"

그리고 당신은 그것을 봅니다. 변수는 생성 중에 내보낼 수 있고( export foo=bar대신 foo=bar), 생성 또는 수정 시 자동으로 내보낼 수 있고( set -a) 나중에 내보낼 수 있으며( var=foo; ...; export var) "내보낼 수 없음"( )일 수 있습니다 export -n var.

쉘이 "실제" 서브쉘을 생성하는 경우( a|b등을 통해) 혼동을 피하기 위해 내보내지 않은 여러 변수를 유지합니다.(a;b)$(a)

답변3

이 명령의 출력은 locale현재 환경의 환경 변수 목록이 아닙니다. 이는 명령 key=value에서 env사용하는 것과 동일한 형식으로 표시되는 프로세스(특정 환경 변수의 영향을 부분적으로 받음)의 유효 로케일을 표시합니다 .

여기에서 eglibc 명령 구현의 소스 코드를 볼 수 있습니다 locale. http://www.eglibc.org/cgi-bin/viewvc.cgi/branches/eglibc-2_19/libc/locale/programs/locale.c?view=markup

관련 정보