如何在 Linux 上读取和设置环境变量和 Shell 变量
介绍
通过 shell 会话与服务器交互时,shell 会编译许多信息以确定其行为和对资源的访问。其中一些设置包含在配置设置中,而其他设置则由用户输入确定。
shell 跟踪所有这些设置和详细信息的一种方法是通过它维护的称为环境的区域。环境是 shell 每次启动包含定义系统属性的变量的会话时构建的区域。
在本指南中,我们将讨论如何与环境交互以及如何通过配置文件以交互方式读取或设置环境变量和 shell 变量。
如果您想使用本地系统或远程服务器继续操作,请打开一个终端并在那里运行本教程中的命令。
环境和环境变量如何工作
每次 shell 会话产生时,都会发生一个进程来收集和编译 shell 进程及其子进程应该可用的信息。它从系统上的各种不同文件和设置中获取这些设置的数据。
环境提供了一种媒介,shell 进程可以通过它获取或设置设置,然后将这些设置传递给它的子进程。
环境实现为表示键值对的字符串。如果传递多个值,它们通常由冒号 (:
) 字符分隔。每对通常看起来像这样:
KEY=value1:value2:...
如果该值包含大量空白,则使用引号:
KEY="value with spaces"
这些场景中的键是变量。它们可以是两种类型之一,环境变量或 shell 变量。
环境变量是为当前 shell 定义并由任何子 shell 或进程继承的变量。环境变量用于将信息传递到从 shell 生成的进程中。
Shell 变量是专门包含在设置或定义它们的 shell 中的变量。它们通常用于跟踪临时数据,例如当前工作目录。
按照惯例,这些类型的变量通常使用全部大写字母定义。这有助于用户区分其他上下文中的环境变量。
打印 Shell 和环境变量
每个 shell 会话都跟踪它自己的 shell 和环境变量。我们可以通过几种不同的方式访问这些。
我们可以使用 env
或 printenv
命令查看所有环境变量的列表。在默认状态下,它们的功能应该完全相同:
- printenv
您的 shell 环境可能设置了更多或更少的变量,其值与以下输出不同:
OutputSHELL=/bin/bash
TERM=xterm
USER=demouser
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca:...
MAIL=/var/mail/demouser
PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
PWD=/home/demouser
LANG=en_US.UTF-8
SHLVL=1
HOME=/home/demouser
LOGNAME=demouser
LESSOPEN=| /usr/bin/lesspipe %s
LESSCLOSE=/usr/bin/lesspipe %s %s
_=/usr/bin/printenv
这是 printenv
和 env
的典型输出。这两个命令之间的区别仅在它们更具体的功能上很明显。例如,使用 printenv
,您可以请求各个变量的值:
- printenv PATH
Output/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
另一方面,env
允许您通过将一组变量定义传递到命令中来修改程序运行的环境,如下所示:
- env VAR1="value" command_to_run command_options
因为,正如我们在上面了解到的,子进程通常会继承父进程的环境变量,这使您有机会覆盖子进程的值或添加其他变量。
从我们的 printenv
命令的输出中可以看出,有相当多的环境变量是通过我们的系统文件和进程设置的,而无需我们的输入。
这些显示了环境变量,但是我们怎么看shell变量呢?
set
命令可用于此目的。如果我们键入 set
而不带任何附加参数,我们将获得所有 shell 变量、环境变量、局部变量和 shell 函数的列表:
- set
OutputBASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
. . .
这通常是一个巨大的列表。您可能希望将其通过管道传输到寻呼机程序中,以便更轻松地处理输出量:
- set | less
我们收到的额外信息量有点大。例如,我们可能不需要知道所有定义的 bash 函数。
我们可以通过指定 set
应该在 POSIX 模式下运行来清理输出,这不会打印 shell 函数。我们可以在子 shell 中执行它,这样它就不会改变我们当前的环境:
- (set -o posix; set)
这将列出所有已定义的环境和 shell 变量。
我们可以尝试将此输出与 env
或 printenv
命令的输出进行比较,以尝试获取仅包含 shell 变量的列表,但由于不同的原因,这将是不完美的这些命令输出信息的方式:
- comm -23 <(set -o posix; set | sort) <(env | sort)
这可能仍然包括一些环境变量,因为 set
命令输出带引号的值,而 printenv
和 env
命令输出不引用字符串的值。
这仍然可以让您对会话中设置的环境变量和 shell 变量有一个很好的了解。
这些变量用于各种各样的事情。它们提供了另一种方法来为进程之间的会话设置持久值,而无需将更改写入文件。
常见的环境和 Shell 变量
一些环境变量和 shell 变量非常有用并且经常被引用。
SHELL
:这描述了将解释您输入的任何命令的 shell。在大多数情况下,这将默认为 bash,但如果您喜欢其他选项,可以设置其他值。< /李>TERM
:指定运行 shell 时要模拟的终端类型。可以针对不同的操作要求模拟不同的硬件终端。不过,您通常不需要担心这一点。USER
:当前登录的用户。PWD
:当前工作目录。OLDPWD
:上一个工作目录。这是由 shell 保存的,以便通过运行cd -
切换回您之前的目录。LS_COLORS
:这定义了颜色代码,用于选择性地向ls
命令添加彩色输出。这用于区分不同的文件类型,并一目了然地向用户提供更多信息。MAIL
:当前用户邮箱的路径。PATH
:系统在查找命令时将检查的目录列表。当用户键入命令时,系统将按此顺序检查目录中的可执行文件。LANG
:当前语言和本地化设置,包括字符编码。HOME
:当前用户的主目录。_
:最近执行的命令。
除了这些环境变量之外,您经常会看到的一些 shell 变量是:
BASHOPTS
:执行 bash 时使用的选项列表。这对于确定 shell 环境是否会按照您希望的方式运行很有用。BASH_VERSION
:正在执行的 bash 版本,采用人类可读的形式。BASH_VERSINFO
:机器可读输出中的 bash 版本。COLUMNS
:用于在屏幕上绘制输出的列宽数。DIRSTACK
:pushd
和popd
命令可用的目录堆栈。HISTFILESIZE
:存储到文件中的命令历史行数。HISTSIZE
:内存中允许的历史命令行数。HOSTNAME
:此时计算机的主机名。IFS
:用于分隔命令行输入的内部字段分隔符。默认情况下,这是一个空格。PS1
:主要命令提示符定义。这用于定义启动 shell 会话时的提示符。PS2
用于声明命令跨越多行时的辅助提示。SHELLOPTS
:可以使用set
选项设置的 Shell 选项。UID
:当前用户的UID。
设置 Shell 和环境变量
为了更好地理解 shell 和环境变量之间的区别,并介绍设置这些变量的语法,我们将做一个小演示。
创建 Shell 变量
我们将从在当前会话中定义一个 shell 变量开始。这很容易实现;我们只需要指定一个名称和一个值。我们将遵守保持变量名全部大写的惯例,并将其设置为一个简单的字符串。
- TEST_VAR='Hello World!'
在这里,我们使用了引号,因为我们的变量值包含一个空格。此外,我们使用了单引号,因为感叹号是 bash shell 中的一个特殊字符,如果它没有被转义或放入单引号中,它通常会扩展到 bash 历史记录中。
我们现在有一个 shell 变量。该变量在我们当前的会话中可用,但不会传递给子进程。
我们可以通过在 set
输出中查找我们的新变量来看到这一点:
- set | grep TEST_VAR
OutputTEST_VAR='Hello World!'
我们可以通过用 printenv
尝试同样的事情来验证这不是一个环境变量:
- printenv | grep TEST_VAR
不应返回任何输出。
让我们借此机会演示访问任何 shell 或环境变量的值的方法。
- echo $TEST_VAR
OutputHello World!
如您所见,通过在变量前加上 $
符号来引用变量的值。 shell 认为这意味着它应该在遇到这个时替换变量的值。
所以现在我们有了一个 shell 变量。它不应该传递给任何子进程。我们可以从当前的 bash shell 中生成一个新的 bash shell 来演示:
- bash
- echo $TEST_VAR
如果我们键入 bash
生成子 shell,然后尝试访问变量的内容,则不会返回任何内容。这是我们所期望的。
通过键入 exit
返回到我们原来的 shell:
- exit
创建环境变量
现在,让我们把我们的 shell 变量变成一个环境变量。我们可以通过导出 变量来做到这一点。这样做的命令被恰当地命名为:
- export TEST_VAR
这会将我们的变量更改为环境变量。我们可以通过再次检查我们的环境列表来检查这一点:
- printenv | grep TEST_VAR
OutputTEST_VAR=Hello World!
这一次,我们的变量出现了。让我们再次尝试使用我们的子 shell 进行实验:
- bash
- echo $TEST_VAR
OutputHello World!
伟大的!我们的子 shell 已收到其父 shell 设置的变量。在我们退出这个子 shell 之前,让我们尝试导出另一个变量。我们可以像这样一步设置环境变量:
- export NEW_VAR="Testing export"
测试它是否作为环境变量导出:
- printenv | grep NEW_VAR
OutputNEW_VAR=Testing export
现在,让我们退出回到原来的 shell:
- exit
让我们看看我们的新变量是否可用:
- echo $NEW_VAR
什么都没有返回。
这是因为环境变量只传递给子进程。没有内置的方法来设置父 shell 的环境变量。这在大多数情况下都很好,可以防止程序影响调用它们的操作环境。
NEW_VAR
变量在我们的子 shell 中设置为环境变量。该变量可用于其自身及其任何子 shell 和进程。当我们退出回到我们的主 shell 时,那个环境被破坏了。
降级和取消设置变量
我们仍然将 TEST_VAR
变量定义为环境变量。我们可以通过键入以下命令将其改回 shell 变量:
- export -n TEST_VAR
它不再是一个环境变量:
- printenv | grep TEST_VAR
但是,它仍然是一个 shell 变量:
- set | grep TEST_VAR
OutputTEST_VAR='Hello World!'
如果我们想完全取消设置一个变量,无论是 shell 还是环境变量,我们可以使用 unset
命令来实现:
- unset TEST_VAR
我们可以验证它不再被设置:
- echo $TEST_VAR
没有返回任何内容,因为变量已被取消设置。
在登录时设置环境变量
我们已经提到许多程序使用环境变量来决定如何操作的细节。我们不希望每次启动新的 shell 会话时都必须设置重要的变量,并且我们已经看到在登录时已经设置了多少变量,那么我们如何自动创建和定义变量呢?
这实际上是一个比最初看起来更复杂的问题,因为 bash shell 会根据其启动方式读取大量配置文件。
登录、非登录、交互式和非交互式 Shell 会话之间的区别
bash shell 根据会话的启动方式读取不同的配置文件。
不同会话之间的一个区别是 shell 是作为登录会话还是非登录会话生成的。
登录 shell 是一个以验证用户身份开始的 shell 会话。如果您登录到终端会话或通过 SSH 并进行身份验证,您的 shell 会话将被设置为登录 shell。
如果您从经过身份验证的会话中启动一个新的 shell 会话,就像我们通过从终端调用 bash
命令所做的那样,将启动一个非登录 shell 会话。当您启动子 shell 时,系统不会要求您提供身份验证详细信息。
另一个可以区分的是 shell 会话是交互式的还是非交互式的。
交互式 shell 会话是附加到终端的 shell 会话。非交互式 shell 会话是一个不附加到终端会话的会话。
因此,每个 shell 会话都被分类为登录或非登录以及交互或非交互。
以 SSH 开头的普通会话通常是交互式登录 shell。从命令行运行的脚本通常在非交互式、非登录 shell 中运行。终端会话可以是这两个属性的任意组合。
shell 会话是分类为登录还是非登录 shell 对读取哪些文件来初始化 shell 会话有影响。
作为登录会话启动的会话将首先从 /etc/profile
文件中读取配置详细信息。然后它将在用户的主目录中查找第一个登录 shell 配置文件以获取用户特定的配置详细信息。
它读取它可以从 ~/.bash_profile
、~/.bash_login
和 ~/.profile
中找到的第一个文件,但不阅读任何进一步的文件。
相反,定义为非登录 shell 的会话将读取 /etc/bash.bashrc
,然后是用户特定的 ~/.bashrc
文件来构建其环境。
非交互式 shell 读取名为 BASH_ENV
的环境变量,并读取指定的文件来定义新环境。
实施环境变量
如您所见,我们通常需要查看各种不同的文件来放置我们的设置。
这提供了很大的灵活性,可以在我们希望在登录 shell 中进行某些设置而在非登录 shell 中进行其他设置的特定情况下提供帮助。然而,大多数时候我们会希望在两种情况下使用相同的设置。
幸运的是,大多数 Linux 发行版都将登录配置文件配置为非登录配置文件的来源。这意味着您可以在非登录配置文件中定义您想要的环境变量。然后将在两种情况下阅读它们。
我们通常会设置特定于用户的环境变量,并且我们通常希望我们的设置在登录和非登录 shell 中都可用。这意味着定义这些变量的地方是在 ~/.bashrc
文件中。
现在打开这个文件:
- nano ~/.bashrc
这很可能已经包含相当多的数据。这里的大部分定义都是为了设置 bash 选项,与环境变量无关。您可以像在命令行中一样设置环境变量:
- export VARNAME=value
任何新的环境变量都可以添加到 ~/.bashrc
文件中的任何位置,只要它们没有放在另一个命令或 for 循环的中间。然后我们可以保存并关闭文件。下次启动 shell 会话时,您的环境变量声明将被读取并传递给 shell 环境。您可以通过键入以下命令强制当前会话现在读取文件:
- source ~/.bashrc
如果您需要设置系统范围的变量,您可能需要考虑将它们添加到 /etc/profile
、/etc/bash.bashrc
或 /等/环境
。
结论
环境变量和 shell 变量始终存在于您的 shell 会话中,并且非常有用。它们是父进程为其子进程设置配置详细信息的一种有趣方式,也是一种在文件外设置选项的方式。
这在特定情况下有很多优点。例如,一些部署机制依赖于环境变量来配置身份验证信息。这很有用,因为它不需要将这些保存在外部各方可能看到的文件中。
还有许多其他更平凡但更常见的场景,您需要读取或更改系统环境。这些工具和技术应该为您进行这些更改和正确使用它们打下良好的基础。