sha*_*ant 19 command-line shell environment-variables
我实际上不知道我可以从命令行访问两种不同类型的变量。我所知道的是,我可以声明如下变量:
foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"
Run Code Online (Sandbox Code Playgroud)
或使用 $ 符号访问它们,例如:
echo $foo
echo ${bar[1]}
Run Code Online (Sandbox Code Playgroud)
或使用内置变量,例如:
echo $PWD
PATH=$PATH:"/usr/bin/myProg"
Run Code Online (Sandbox Code Playgroud)
现在,我听说有两种(至少?)变量类型:shell 变量和环境变量。
Kus*_*nda 19
Shell 变量是范围在当前 Shell 会话中的变量,例如在交互式 Shell 会话或脚本中。
您可以通过为未使用的名称赋值来创建 shell 变量:
var="hello"
Run Code Online (Sandbox Code Playgroud)
shell 变量的用途是跟踪当前会话中的数据。Shell 变量的名称通常带有小写字母。
环境变量是已导出的外壳变量。这意味着它将作为变量可见,不仅在创建它的 shell 会话中,而且对于从该会话启动的任何进程(不仅仅是 shell)。
VAR="hello" # shell variable created
export VAR # variable now part of the environment
Run Code Online (Sandbox Code Playgroud)
或者
export VAR="hello"
Run Code Online (Sandbox Code Playgroud)
导出 shell 变量后,它会保持导出状态,直到它被取消设置,或者直到它的“导出属性”被删除(使用export -n
in bash
),因此通常不需要重新导出它。取消设置变量unset
会删除它(无论它是否是环境变量)。
bash
和其他 shell 中的数组和关联哈希可能不会导出为环境变量。环境变量必须是值是字符串的简单变量,并且它们的名称通常由大写字母组成。
环境变量的用途是跟踪当前 shell 会话中的数据,但也允许任何启动的进程获取该数据的一部分。这种情况的典型情况是PATH
环境变量,它可以在 shell 中设置,以后任何想要启动程序而不指定它们的完整路径的程序都会使用它。
进程中环境变量的集合通常被称为“进程的环境”。每个进程都有自己的环境。
环境变量只能“转发”,即子进程永远不能改变其父进程中的环境变量,并且除了在子进程启动时为其设置环境外,父进程不能改变其现有环境子进程。
环境变量可以与env
(不带任何参数)一起列出。除此之外,它们在 shell 会话中看起来与非导出的 shell 变量相同。这对于 shell 来说有点特殊,因为大多数其他编程语言通常不会将“普通”变量与环境变量混合(见下文)。
env
也可用于在进程环境中设置一个或多个环境变量的值,而无需在当前会话中设置它们:
env CC=clang CXX=clang++ make
Run Code Online (Sandbox Code Playgroud)
这首先make
将环境变量CC
设置为 valueclang
并CXX
设置为clang++
。
它还可用于清除进程的环境:
env -i bash
Run Code Online (Sandbox Code Playgroud)
这会启动bash
但不会将当前环境转移到新bash
进程(它仍然具有环境变量,因为它从其 shell 初始化脚本创建新的)。
$ var="hello" # create shell variable "var"
$ bash # start _new_ bash session
$ echo "$var" # no output
$ exit # back to original shell session
$ echo "$var" # "hello" is outputted
$ unset var # remove variable
$ export VAR="hello" # create environment variable "VAR"
$ bash
$ echo "$VAR" # "hello" is outputted since it's exported
$ exit # back to original shell session
$ unset VAR # remove variable
$ ( export VAR="hello"; echo "$VAR" ) # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR" # no output since a subshell has its own environment
Run Code Online (Sandbox Code Playgroud)
大多数编程语言中都有允许获取和设置环境变量的库函数。请注意,由于环境变量存储为简单的键值关系,因此它们通常不是语言的“变量”。程序可以获取与键(环境变量的名称)对应的值(始终是字符串),但随后必须将其转换为整数或语言期望该值具有的任何数据类型。
在C,环境变量可以使用访问getenv()
,setenv()
,putenv()
和unsetenv()
。使用这些例程创建的变量由 C 程序启动的任何进程以相同的方式继承。
其他语言可能有特殊的数据结构完成同样的事情,就像%ENV
在Perl哈希,或ENVIRON
在大多数实现关联数组awk
。
jll*_*gre 16
环境变量是name=value
存在于任何程序(shell、应用程序、守护进程……)的对的列表。它们通常由子进程继承(由fork
/exec
序列创建):子进程获得自己的父变量副本。
Shell 变量确实仅存在于 Shell 的上下文中。它们仅在子外壳中继承(即当外壳在没有exec
操作的情况下分叉时)。根据 shell 的特性,变量可能不仅是像环境字符串这样的简单字符串,还可能是数组、复合、类型变量(如整数或浮点数)等。
当 shell 启动时,它从其父级继承的所有环境变量也成为 shell 变量(除非它们作为 shell 变量和其他极端情况无效,例如IFS
由某些 shell 重置),但这些继承的变量被标记为导出1。这意味着它们将保持可用于具有 shell 设置的潜在更新值的子进程。对于在 shell 下创建并用export
关键字标记为导出的变量,情况也是如此。
数组和其他复杂类型变量不能被导出,除非它们的名称和值可以转换为name=value
模式,或者当一个特定于 shell 的机制到位时(例如:bash
导出环境中的函数和一些奇异的非 POSIX shell,例如rc
并且es
可以导出数组)。
因此,环境变量和 shell 变量之间的主要区别在于它们的作用域:环境变量是全局的,而非导出的 shell 变量是脚本的本地变量。
另请注意,现代 shell(至少ksh
和bash
)支持第三个 shell 变量范围。与功能创建变量typeset
的关键字是局部的功能(函数声明的方式,启用/禁用下此功能ksh
,并且持续性的行为之间是不同的bash
和ksh
)。见https://unix.stackexchange.com/a/28349/2594
1这适用于现代的炮弹一样ksh
,dash
,bash
和类似的。传统的 Bourne shell 和非 Bourne 语法 shellcsh
具有不同的行为。
Shell 变量很难复制。
$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$
Run Code Online (Sandbox Code Playgroud)
但是环境变量可以复制;它们只是一个列表,一个列表可以有重复的条目。这就是envdup.c
这样做的。
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[]) {
char **newenv;
int envcount = 0;
if (argc < 2) errx(64, "Usage: envdup command [args ..]");
newenv = environ;
while (*newenv++ != NULL) envcount++;
newenv = malloc(sizeof(char *) * (envcount + 3));
if (newenv == NULL) err(1, "malloc failed");
memcpy(newenv, environ, sizeof(char *) * envcount);
newenv[envcount] = "FOO=bar";
newenv[envcount+1] = "FOO=zot";
newenv[envcount+2] = NULL;
environ = newenv;
argv++;
execvp(*argv, argv);
err(1, "exec failed '%s'", *argv);
}
Run Code Online (Sandbox Code Playgroud)
我们可以编译并运行告诉envdup
然后运行env
以向我们展示设置了哪些环境变量......
$ make envdup
cc envdup.c -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$
Run Code Online (Sandbox Code Playgroud)
这可能仅用于查找程序处理方式中的错误或其他奇怪之处**environ
。
$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$
Run Code Online (Sandbox Code Playgroud)
看起来这里的 Python 3.6 会盲目地传递重复项(一种泄漏的抽象),而 Perl 5.24 则不会。贝壳呢?
$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot'
bar
FOO=bar
$
Run Code Online (Sandbox Code Playgroud)
天哪,如果sudo
只清理第一个环境条目然后bash
运行第二个环境会发生什么?你好PATH
或LD_RUN_PATH
利用。您的sudo
(以及其他所有东西?)是否为那个洞打了补丁?安全漏洞既不是“轶事差异”,也不是调用程序中的“错误”。