介绍
当通过 shell 会话与您的服务器交互时,您的 shell 会编译许多信息来确定其行为和对资源的访问。其中一些设置包含在配置设置中,其他设置由用户输入确定。
shell 跟踪所有这些设置和细节的一种方法是通过它维护的一个称为环境的区域。环境是 shell 在每次启动包含定义系统属性的变量的会话时构建的区域。
在本指南中,我们将讨论如何与环境交互以及如何通过配置文件交互地读取或设置环境和 shell 变量。
要使用浏览器中的终端来学习本教程,请单击Launch an Interactive Terminal!
下面的按钮:
否则,如果您想继续使用本地系统或远程服务器,请打开终端并在那里运行本教程中的命令。
环境和环境变量如何工作
每次产生 shell 会话时,都会发生一个进程来收集和编译 shell 进程及其子进程应该可用的信息。它从系统上的各种不同文件和设置中获取这些设置的数据。
环境提供了一种媒介,通过该媒介,shell 进程可以获取或设置设置,然后将这些设置传递给其子进程。
该环境被实现为表示键值对的字符串。如果传递多个值,它们通常用冒号 ( :
) 字符分隔。每对通常看起来像这样:
KEY=value1:value2:...
如果该值包含重要的空格,则使用引号:
KEY="value with spaces"
这些场景中的关键是变量。它们可以是两种类型之一,环境变量或外壳变量。
环境变量是为当前 shell 定义的变量,由任何子 shell 或进程继承。环境变量用于将信息传递到从 shell 产生的进程中。
Shell 变量是专门包含在设置或定义它们的Shell 中的变量。它们通常用于跟踪临时数据,例如当前工作目录。
按照惯例,这些类型的变量通常使用全部大写字母定义。这有助于用户区分其他上下文中的环境变量。
打印外壳和环境变量
每个 shell 会话都会跟踪自己的 shell 和环境变量。我们可以通过几种不同的方式访问这些。
我们可以使用env
orprintenv
命令查看所有环境变量的列表。在默认状态下,它们的功能应该完全相同:
- 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
orprintenv
命令的输出进行比较,以尝试获取仅包含 shell 变量的列表,但由于这些命令输出信息的方式不同,这将是不完美的:
- comm -23 <(set -o posix; set | sort) <(env | sort)
这可能仍然包括一些环境变量,因为set
命令输出引用的值,而printenv
和env
命令不引用字符串的值。
这仍然可以让您对会话中设置的环境和 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 变量。这很容易实现;我们只需要指定一个名称和一个值。我们将遵守保持变量名称全部大写的约定,并将其设置为一个简单的字符串。
- 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 之前,让我们尝试导出另一个变量。我们可以在一个步骤中设置环境变量,如下所示:
- 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 根据会话的启动方式读取不同的配置文件。
不同会话之间的一个区别在于外壳是否正在催生作为登录或非登录会话。
甲登录壳是由认证用户开始一个壳会话。如果您正在登录终端会话或通过 SSH 进行身份验证,您的 shell 会话将被设置为登录 shell。
如果您从经过身份验证的会话中启动一个新的 shell 会话,就像我们通过bash
从终端调用命令所做的那样,将启动一个非登录的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 会话中,并且非常有用。它们是父进程为其子进程设置配置详细信息的一种有趣方式,并且是在文件之外设置选项的一种方式。
这在特定情况下具有许多优点。例如,一些部署机制依赖环境变量来配置认证信息。这很有用,因为它不需要将这些保存在外部各方可能看到的文件中。
还有许多其他更平凡但更常见的场景,您需要阅读或更改系统环境。这些工具和技术应该为您进行这些更改和正确使用它们提供良好的基础。