介绍

当通过 shell 会话与服务器交互时,shell 会编译许多信息来确定其行为和对资源的访问。其中一些设置包含在配置设置中,而其他设置则由用户输入确定。

shell 跟踪所有这些设置和详细信息的一种方法是通过它维护的称为环境的区域。环境是 shell 每次启动会话时构建的区域,其中包含定义系统属性的变量。

在本指南中,我们将讨论如何与环境交互以及如何通过配置文件交互地读取或设置环境和 shell 变量。

如果您想使用本地系统或远程服务器进行操作,请打开终端并运行本教程中的命令。

环境和环境变量如何工作


每次生成 shell 会话时,都会发生一个过程来收集和编译 shell 进程及其子进程可用的信息。它从系统上的各种不同文件和设置获取这些设置的数据。

该环境提供了一种媒介,shell 进程可以通过该媒介获取或设置设置,然后将这些设置传递给其子进程。

该环境被实现为表示键值对的字符串。如果传递多个值,它们通常用冒号 ( :) 字符分隔。每对通常看起来像这样:

KEY=value1:value2:...

如果该值包含大量空格,则使用引号:

KEY="value with spaces"

这些场景中的关键是变量。它们可以是两种类型之一:环境变量或 shell 变量。

环境变量是为当前 shell 定义的变量,并由任何子 shell 或进程继承。环境变量用于将信息传递到从 shell 生成的进程中。

Shell 变量是专门包含在设置或定义它们的 shell 中的变量。它们通常用于跟踪临时数据,例如当前工作目录。

按照惯例,这些类型的变量通常使用全部大写字母来定义。这有助于用户区分其他上下文中的环境变量。

打印外壳和环境变量

每个 shell 会话都会跟踪自己的 shell 和环境变量。我们可以通过几种不同的方式访问这些内容。

我们可以使用envprintenv命令查看所有环境变量的列表。在默认状态下,它们的功能应该完全相同:

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 函数。

我们可以通过指定应该在 POSIX 模式下运行来清理输出set,这不会打印 shell 函数。我们可以在子 shell 中执行它,这样它就不会改变我们当前的环境:

(set -o posix; set)

这将列出所有已定义的环境变量和 shell 变量。

env我们可以尝试将此输出与或命令的输出进行比较printenv,以尝试获取仅包含 shell 变量的列表,但由于这些命令输出信息的方式不同,这将是不完美的:

comm -23 <(set -o posix; set | sort) <(env | sort)

这可能仍然包括一些环境变量,因为该set命令输出带引号的值,而printenvenv命令不引用字符串的值。

这仍然可以让您很好地了解会话中设置的环境和 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:用于在屏幕上绘制输出的列宽数。
  • DIRSTACKpushd:可使用和命令使用的目录堆栈popd
  • HISTFILESIZE:存储到文件的命令历史记录的行数。
  • HISTSIZE:内存中允许的命令历史记录行数。
  • HOSTNAME:此时计算机的主机名。
  • IFS:内部字段分隔符,用于分隔命令行上的输入。默认情况下,这是一个空格。
  • PS1:主要命令提示符定义。这用于定义启动 shell 会话时的提示符。用于PS2声明命令跨越多行时的辅助提示。
  • SHELLOPTS:可以使用选项设置的 shell 选项set
  • 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,然后尝试访问变量的内容,则不会返回任何内容。这正是我们所期望的。

输入以下命令返回我们原来的 shell exit

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

现在,让我们退出回到原来的 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 会影响读取哪些文件来初始化 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/etc/environment

结论

环境和 shell 变量始终存在于 shell 会话中,并且非常有用。它们是父进程为其子进程设置配置详细信息的一种有趣方式,也是在文件外部设置选项的一种方式。

这在特定情况下有很多优点。例如,某些部署机制依赖环境变量来配置身份验证信息。这很有用,因为它不需要将这些内容保存在外部各方可以看到的文件中。

还有许多其他更平凡但更常见的场景,您需要读取或更改系统环境。这些工具和技术应该为您进行这些更改并正确使用它们提供良好的基础。

By zk

一个程序员

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注