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"
출력이 전송되지 않습니다. 일반적으로 locale
13개의 LC_*
변수가 표시되지만 env
9개만 표시됩니다.
$ 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
호출된 프로그램의 매개변수, 세 번째 매개변수는 환경 변수입니다.
예를 들어, 세 번째 인수에서는 PS1
or does not listed 입니다 LC_TYPE
.
일반적으로 에 전송되는 환경 변수 목록에 나타나 env
거나 나타나는 변수입니다 . 일부 변수는 또는 에 표시되지만 다른 변수는 표시되지 않습니다( 및 및 그러나 null 값이 있음). 마지막으로, 기타 변수는 눈에 보이는 효과( )가 있지만 반영된 대로 정의되지 않습니다 .set
execve
locale
env
set
LC_TYPE
LC_COLLATE
LC_MESSAGE
LC_ALL
env
PS1
set
여기서 무슨 일이 일어나고 있는 걸까요? (매개변수 없음), (분명히 로케일 변수만 고려됨) env
의 차이점은 무엇입니까?set
locale
답변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 execv
man 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
쉘은 두 가지 유형의 변수를 알고 있습니다.
쉘(및 서브쉘)에만 알려진 "내부" 변수
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