env 和 printenv 显示哪些环境变量/shell 变量

Tra*_*iet 1 environment-variables

我已经看到了相关的问题,比如这个,但他们没有提供确切的回答我的问题

从我的实验以及这个答案printenvenv几乎表现出相同的设定的系统变量。

如果我将变量设置在

  1. /etc/bash.bashrc(应该用于系统范围的系统变量)

    SYSTEM_ENVI=1000
    
    Run Code Online (Sandbox Code Playgroud)
  2. ~/.bashrc (应该用于用户特定的系统变量)

    USER_ENVI=10
    
    Run Code Online (Sandbox Code Playgroud)

我什至注销并登录,以便 /etc/environment 生效。发生以下场景:

$echo $SYSTEM_ENVI
//outputs 1000
$echo $USER_ENVI
//outputs 10
$CURR_ENVI=1
$env | grep USER_ENVI
//nothing shows up, the same if I grepped SYSTEM_ENVI or CURR_ENVI
$set | grep USER_ENVI
//shows up USER_ENVI assignment, the same if I grepped SYSTEM_ENVI or CURR_ENVI
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 什么系统变量做printenv/env打印?
  2. 应该使用set查看所有可访问的变量(系统变量和局部变量)而不是printenvorenv吗?

关于不重复的理由

就我而言,这个问题和明显的答案帮助我意识到以下事实:

  1. Shell变量不是环境变量
  2. /etc/bash.bashrc~/.bashrc 中的赋值不会创建环境变量,而是指示交互式非登录 shell 进程在启动时创建和初始化这些shell 变量

我认为我的问题不一定与这个问题不同,但是阅读该问题的标记答案并没有像这篇文章中给出的答案那样让我满意。

Sté*_*las 5

env并且printenv正在打印由执行它们的命令提供给它们的环境字符串列表(旨在包含环境变量定义)。调用者最终会做一个:

execve("/usr/bin/env", argv, envp);
Run Code Online (Sandbox Code Playgroud)

系统调用 其中argvenvp是两个字符串列表。

env/printenv只打印 中的字符串列表envp,每行一个。

按照惯例,在字符串envp的格式var=value,但他们没有要(我不知道任何的execve()执行,强制执行的话),最envprintenv实现没有时,他们显示他们不在乎。

当调用者是一个 POSIX shell 时,它将包含在envp它传递给env它的被标记为导出的shell 变量列表中(要么因为用户调用了export/ typeset -x,要么因为变量已经在 shell 的环境中)启动时收到)。

如果 shell 在启动时收到的某些环境变量无法映射到 shell 变量,或者如果envp它收到的任何字符串不包含=字符,则取决于 shell 实现,这些字符串将被传递原封不动,否则外壳会剥去它们或其中的一些。

以 为例bash,使用 GNUenv传递任意变量名称列表(env虽然不能传递任意 envp 字符串,但它们必须包含=,并且使用的setenv()不能传递一些以=¹开头的字符串)。

$ env -i '=foo' '1=x' '+=y' bash -c printenv
+=y
1=x
[...]
Run Code Online (Sandbox Code Playgroud)

(名称为空的变量已被删除,但其他变量未删除)。

此外,如果 shell 收到多个envp字符串的相同变量名称,根据 shell,它们将全部传递,或者仅传递第一个,或仅传递最后一个。

set in POSIX shell 打印 shell 变量列表,包括支持数组/散列类型的 shell 的非标量变量,无论它们是否已标记为导出。

在 POSIX shell 中,您还可以export -p用来列出已标记为导出的变量。与env/相反printenv,它还列出了已标记为导出但尚未赋予任何值的变量。

在类似 Korn 的 shell 中,例如ksh, zshor bash,您还可以使用它typeset来获取更多信息,包括变量的属性,以及按类型列出变量(例如typeset -a列出数组变量)。

在这里,通过添加USER_ENVI=10到您的~/.bashrc,您正在配置bashshell的交互式非登录调用,以在启动时定义一个USER_ENVI shell变量。由于您没有使用export,该变量仍然是一个 shell 变量(除非它在bash启动时在环境中),所以它不会作为环境变量传递给由该 shell 执行的命令。

/etc/environment本身,在 Ubuntu 16.04 上由pam_env.so可插拔身份验证模块读取。用来记录你在喜欢的应用程序loginsshdlightdm将如果配置了读取这些文件pam_env.so/etc/pam.d,并通过相应的环境变量(无关这里shell变量),他们在通过身份验证后(如登录shell你的名字启动命令login/ sshd,或您的图形会话管理器lightdm...)。

由于环境是默认继承的,当你的会话管理器执行一个终端模拟器,然后执行你的登录 shell 时,这些环境变量将在每一步传递,你的 shell 会将它们映射到 shell 变量,你可以在命令中扩展这些变量符合之类的东西echo "$VAR"

pam_envenv 文件/etc/environment看起来像 shell 脚本,但pam_env不调用 shell 来解析它们并且只理解 shell 语法的一个子集,并且只允许定义名称由一个或多个 ASCII 字母数字字符或下划线组成的变量(它确实让您定义一个123变量,尽管它不是有效的 POSIX shell 变量名)。


¹,要传递任意环境字符串的列表,您还可以execve()直接调用:

perl -e 'require "syscall.ph";
         $cmd = "/bin/zsh";
         $args = pack("p*x[p]", "sh", "-c", "printenv");
         $env = pack("p*x[p]", "a=b", "a=c", "", "+=+", "=foo", "bar");
         syscall(SYS_execve(), $cmd, $args, $env)'
Run Code Online (Sandbox Code Playgroud)

在这里测试zsh而不是bash