Per*_*-lk 7 shell process environment-variables locale
试图了解 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
Run Code Online (Sandbox Code Playgroud)
但是,如果我发现,例如,LC_CTYPE使用env | grep "LC_CTYPE",它不会发送任何输出。一般来说,locale向我展示了 13 个LC_*变量,但env只有 9 个:
$ locale | grep "LC_*" | wc -l
13
$ env | grep "LC_*" | wc -l
9
Run Code Online (Sandbox Code Playgroud)
其他具有不同“性质”的变量是PS1. 例如:
$ env | grep "PS1" # No output, but...
$ set | grep "PS1" | head -n 1
PS1=$'\\[\\033[1;33m\\][\\t][\\W]\342\230\233\\[\\033[0m\\] '
Run Code Online (Sandbox Code Playgroud)
当然,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) = ?
Run Code Online (Sandbox Code Playgroud)
shell 在执行程序时所做的第一件事是调用execve,它确实调用了一个程序。它的第一个参数是被调用的程序,第二个是被argv调用程序的参数,第三个参数是环境变量。
在该第三参数,例如,不出现任何PS1或LC_TYPE。
通常,变量出现在env或set出现在发送到 的环境变量列表中execve。某些locale变量出现在envor 中,set但其他变量没有出现( LC_TYPE, LC_COLLATEand LC_MESSAGE, 以及LC_ALL但具有空值)。最后,其他变量env虽然具有可见效果 ( PS1),但并未在 中定义,如 所示set。
这里发生了什么?env, set(不带参数), locale(显然只考虑语言环境变量)之间有什么区别?
gol*_*cks 15
这里的主要问题——解释了为什么,例如,$PS1没有被报告env——是env来自非交互式环境的报告。进程是从交互式shell 的一个分支执行的,但是它们的环境的设置方式有一个微妙之处:它实际上是通过为所有exec()“d 进程”设置的本机 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;
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,如果您编译并运行它,您会发现内容**environ与报告的内容完全匹配env:
$ gcc test.c
$ ./a.out > aout.txt
$ env > env.txt
$ diff env.txt aout.txt
68c68
< _=/bin/env
---
> _=./a.out
Run Code Online (Sandbox Code Playgroud)
唯一的区别是可执行文件的名称。那么它**environ来自哪里,为什么它不包含,例如,$PS1?
基本的解释是进程总是作为其他进程的子进程创建并且它们继承**environ,但PS1从来不是它的一部分。启动时,shell 可能会从标准位置获取变量,这些位置根据 shell 是否交互而有所不同;看到INVOCATION在man bash。这方面的一个方面是:
如果 bash 是交互式的,则设置 PS1 [...] ,允许 shell 脚本或启动文件测试此状态。
现在,注意这样的/etc/bashrc事情:
# are we an interactive shell?
if [ "$PS1" ]; then
Run Code Online (Sandbox Code Playgroud)
这是您设置实际(花哨)提示的地方,它和 的初始值$PS1都没有被export编辑过。初始值是由 shell 在调用时创建的,因为它是交互式的,然后它获取该文件 - 但PS1没有放入**environ. 如果您执行,您可以看到这一点:
#!/bin/sh
echo $PS1
Run Code Online (Sandbox Code Playgroud)
什么都没有——即使你echo $PS1在你的交互式 shell 中定义了它。这是因为**environ执行#!/bin/sh的 与父交互式 shell 的相同,但不包含PS1. 这意味着每个 shell 使用一个单独的全局变量内部表,但最初填充的是 from **environ(这很令人困惑,因为这意味着**environ不包括许多称为环境变量的东西)。
的内容**environ在 in 中/proc/[PID]/environ,如果您检查当前交互式 shell 的内容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;
}
Run Code Online (Sandbox Code Playgroud)
MYFOO=whatbar?将在输出中(请参阅man putenv)。由于 shell 通过fork()ing(复制父进程的内存堆栈)然后调用execv()(传递复制的**environ)来创建进程,我们可以看到一种机制,通过该机制可以将环境变量exported 到子进程。
如果你把 afork()扔进那个例子,你会看到情况就是这样,并且(重申),这个 fork'ing 和潜在的 exec'ing 过程是创建子进程并**environ从它们的祖先那里继承的过程。exec调用替换了过程映像,但根据man execv和man environ(注意,前者的某些版本不涉及此),**environ由系统传递。
这是一个字面 fork 和 exec 的/usr/bin/envwithMYFOO=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;
}
Run Code Online (Sandbox Code Playgroud)
它是特定 shell 实例的私有数据。Bash 将通过set不带参数的方式向您展示这个 + 继承的环境内容。请注意,此输出还包括源函数。
但是,如果我发现,例如,LC_CTYPE 使用 env | grep "LC_CTYPE",它不发送输出。通常,语言环境向我显示了 13 个 LC_* 变量,而 env 仅显示了 9 个:
我根本没有LC_从env(just LANG)得到任何变量,但从locale. 我认为这些是通过locale调用设置的变量而不是导出的;您从中获取任何信息的事实env可能反映了某处某些配置中的幼稚错误。
| 归档时间: |
|
| 查看次数: |
2765 次 |
| 最近记录: |