正如我们之前讨论的,shell在shell会话期间维护一组名为 environment ,环境,的信息。程序使用存储在环境中的数据来确定有关系统配置的事实。虽然大多数程序使用 configuration files ,配置文件,来存储程序设置,但一些程序也会查找存储在环境中的值来调整其行为。知道这一点后,我们可以使用环境来定制我们的shell体验。
本章将会使用以下命令:
printenv
—— 打印部分或全部环境set
—— 设置shell选项export
—— 将环境导出到随后执行的程序alias
—— 为命令创建别名source
—— 从当前shell中的文件执行命令第十一章:环境环境中储存了什么?检查环境一些有趣的变量环境是如何建立的?启动文件中有什么?探索子进程如何继承他们的环境在临时环境中启动项目修改环境我们应该修改哪些文件?文本编辑器使用文本编辑器为什么注释很重要激活我们的更改更多关于Source总结
shell在环境中存储两种基本类型的数据;不过,使用 bash
,乍一看,这些类型基本上无法区分。它们是环境变量(environment variables)和shell变量(shell variables)。shell变量是 bash
当前实例放置在那里的数据,环境变量是其他一切。除了变量之外,shell还存储一些编程数据,即别名(aliase)和shell函数(shell functions)。我们在第5章“使用命令”中介绍了别名,我们将在第4部分中介绍shell函数(与shell脚本相关)。
要查看环境中存储的内容,我们可以使用 bash
中内置的 set
或 printenv
程序。 set
命令将显示shell和环境变量,而 printenv
将仅显示后者。由于环境内容列表相当长,最好将任一命令的输出到 less
。
xxxxxxxxxx
[me@linuxbox ~]$ printenv | lessDoing so, we should get something that looks like this:
USER=me
PAGER=less
LSCOLORS=Gxfxcxdxbxegedabagacad
XDG_CONFIG_DIRS=/etc/xdg/xdg-ubuntu:/usr/share/upstart/xdg:/etc/xdg
PATH=/home/me/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
DESKTOP_SESSION=ubuntu
QT_IM_MODULE=ibus
QT_QPA_PLATFORMTHEME=appmenu-qt5
JOB=dbus
PWD=/home/me
GNOME_KEYRING_PID=1850
LANG=en_US.UTF-8
GDM_LANG=en_US
MANDATORY_PATH=/usr/share/gconf/ubuntu.mandatory.path
MASTER_HOST=linuxbox
IM_CONFIG_PHASE=1
COMPIZ_CONFIG_PROFILE=ubuntu
GDMSESSION=ubuntu
SESSIONTYPE=gnome-session
XDG_SEAT=seat0
HOME=/home/me
SHLVL=2
LANGUAGE=en_US
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LESS=-R
LOGNAME=me
COMPIZ_BIN_PATH=/usr/bin/
LC_CTYPE=en_US.UTF-8
XDG_DATA_DIRS=/usr/share/ubuntu:/usr/share/gnome:/usr/local/share/:/usr/share/
QT4_IM_MODULE=xim
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-IwaesmWaT0
LESSOPEN=| /usr/bin/lesspipe %s
INSTANCE=
我们看到的是一个环境变量及其值的列表。例如,我们看到一个名为 USER
的变量,它包含值 me
。 printenv
命令还可以列出特定变量的值。
xxxxxxxxxx
[me@linuxbox ~]$ printenv USER
me
set
命令在没有选项或参数的情况下使用时,将显示shell和环境变量,以及任何定义的shell函数。与 printenv
不同,它的输出是按照字母顺序礼貌(courteously)排序的。
xxxxxxxxxx
[me@linuxbox ~]$ set | less
也可以使用 echo
命令查看变量的内容,如下所示:
xxxxxxxxxx
[me@linuxbox ~]$ echo $HOME
/home/me
set
和 printenv
都不显示的环境元素之一是别名(aliases)。要查看它们,请输入不带参数的 alias
命令。
xxxxxxxxxx
[me@linuxbox ~]$ alias
alias l.='ls -d .* --color=tty'
alias ll='ls -l --color=tty'
alias ls='ls --color=tty'
alias vi='vim'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
环境包含相当多的变量(variables),尽管环境与这里介绍的环境不同,但我们可能会在我们的环境中看到下表中列出的变量。
变量 | 内容 |
---|---|
DISPLAY | 如果我们正在运行图形环境,则表示显示的名称。通常这是 :0 ,表示X服务器生成的第一个显示。 |
EDITOR | 用于文本编辑的程序的名称。 |
SHELL | 用户默认shell程序的名称。 |
HOME | 我们主目录的路径名。 |
LANG | 定义人类语言的字符集和排序顺序。 |
OLDPWD | 上一个工作目录。 |
PAGER | 用于分页输出的程序名称。这通常设置为 /usr/bin/less 。 |
PATH | 一个用冒号分隔的目录列表,当我们输入可执行程序的名称时,会搜索这些目录。 |
PS1 | 这代表“prompt string(提示字符串)1”。它定义了shell提示的内容。正如我们将在第13章中看到的,这可以进行广泛的定制。 |
PWD | 当前工作路径。 |
TERM | 终端类型的名称。类Unix系统支持多种终端协议;此变量设置与我们的终端仿真器一起使用的协议。 |
TZ | 指定我们的时区。类Unix系统将计算机的内部时钟保持在协调世界时(Coordinated Universal Time,UTC),然后通过应用此变量指定的偏移量来显示本地时间。 |
USER | 用户名 |
如果缺少这些值,请不要担心。它们因发行版而异。
当我们登录到系统时, bash
程序启动,并读取一系列称为启动文件(startup files)的配置脚本,这些脚本定义了所有用户共享的默认环境。随后,我们的主目录中有更多的启动文件,用于定义我们的个人环境。确切的顺序取决于正在启动的shell会话的类型。有两种。
登录shell会话
登录shell会话是提示我们输入用户名和密码的会话。例如,当我们登录到图形环境时,就会发生这种情况。当我们启动虚拟控制台会话时,也会执行此操作。
非登录shell会话
当我们使用终端模拟器在GUI中启动终端会话时,通常会发生非登录shell会话。
登录shell读取一个或多个启动文件,如下表所示:
文件 | 内容 |
---|---|
/etc/profile | 适用于所有用户的全局配置脚本。 |
~/.bash_profile | 用户的个人启动文件。这可用于扩展或覆盖全局配置脚本中的设置。 |
~/.bash_login | 如果找不到 ~/.bash_profile ,bash将尝试读取此脚本。 |
~/.profile | 如果未找到 ~/.bash_profile 或 ~/.bash_login ,bash将尝试读取此文件。这是基于Debian的发行版(如Ubuntu)的默认设置。 |
非登录shell会话读取下表中列出的启动文件:
文件 | 内容 |
---|---|
/etc/bash.bashrc | 适用于所有用户的全局配置脚本。 |
~/.bashrc | 用户的个人启动文件。它可用于扩展或覆盖全局配置脚本中的设置。 |
除了读取上表中的启动文件外,非登录shell还从其父进程(通常是登录shell)继承环境变量。
看看安装了哪些启动文件。请记住,由于上面列出的大多数文件名都以句点开头(这意味着它们是隐藏的),因此在使用 ls
时,我们需要使用 -a
选项。
从普通用户的角度来看, ~/.bashrc 文件可能是最重要的启动文件,因为它几乎总是被读取的。默认情况下,非登录shell会读取它,登录shell的大多数启动文件都是以读取 ~/.bashrc 文件的方式编写的。
如果我们查看一个典型的 .bash_profile (取自CentOS 6系统),它看起来像这样:
xxxxxxxxxx
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
以“#”开头的行是注释,不会被shell读取。这些是为了人类可读性。第一个有趣的事情发生在第四行,代码如下:
xxxxxxxxxx
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
这被称为 if复合命令(if compound command),当我们在第4部分中学习shell脚本时,我们将全面介绍它,但现在,这里有一个翻译:
xxxxxxxxxx
If the file "~/.bashrc" exists, then
read the "~/.bashrc" file.
我们可以看到,这段代码是登录shell获取 .bashrc 内容的方式。启动文件中的下一件事与 PATH
变量有关。
你有没有想过,当我们在命令行上输入命令时,shell是如何知道在哪里找到命令的?例如,当我们输入 ls
时,shell不会搜索整个计算机以查找 /bin/ls ( ls
命令的完整路径名);相反,它搜索 PATH
变量中包含的目录列表。
PATH
变量通常(但并不总是,取决于发行版)由 /etc/profile 启动文件使用以下代码设置:
xxxxxxxxxx
PATH=$PATH:$HOME/bin
修改 PATH
以将目录 $HOME/bin 添加到列表末尾。这是我们在【第7章.以Shell的眼光看待世界】中提到的参数扩展的一个例子。要演示其工作原理,请尝试以下操作:
xxxxxxxxxx
[me@linuxbox ~]$ foo="This is some "
[me@linuxbox ~]$ echo $foo
This is some
[me@linuxbox ~]$ foo=$foo"text."
[me@linuxbox ~]$ echo $foo
This is some text.
使用这种技术,我们可以将文本附加到变量内容的末尾。
通过将字符串 $HOME/bin 添加到 PATH
变量内容的末尾,目录 $HOME/bin 将添加到输入命令时搜索的目录列表中。这意味着,当我们想在主目录中创建一个目录来存储我们自己的私有程序时,shell已经准备好容纳我们了。我们所要做的就是将其称为 bin
,我们就可以开始了。
注意:许多发行版默认提供此 PATH
设置。基于Debian的发行版,如Ubuntu,在登录时测试 ~/bin 目录的存在,如果找到该目录,则将其动态添加到 PATH
变量中。
最后有一行:
xxxxxxxxxx
export PATH
export
命令告诉shell使 PATH
的内容可用于此shell的子进程。从某种意义上说,它将shell变量转换为环境变量。
最后一点值得进一步阐述。Shell变量是Shell当前实例的局部变量,不会复制到Shell启动的任何子实例。让我们来证明一下。
首先,我们将在当前shell中设置一个shell变量:
xxxxxxxxxx
[me@linuxbox ~]$ foo="bar"
接下来,我们将启动shell的另一个副本:
xxxxxxxxxx
[me@linuxbox ~]$ bash
[me@linuxbox ~]$
现在看起来什么都没发生,但实际上我们正在运行shell的另一个实例。我们可以通过查看 ps
命令的结果来证明这一点:
xxxxxxxxxx
[me@linuxbox ~]$ ps
PID TTY TIME CMD
1011638 pts/9 00:00:00 bash
1011650 pts/9 00:00:00 bash
1011662 pts/9 00:00:00 ps
在这里,我们看到我们正在运行两个bash副本。由于我们在启动新实例时没有将其放入后台,因此现在它是前台任务,原始实例正在等待这个新shell完成。现在,让我们尝试查看我们刚才设置的变量 foo
的值:
xxxxxxxxxx
[me@linuxbox ~]$ echo $foo
[me@linuxbox ~]$
没有结果表明 foo
变量为空。原因是我们在这个shell实例中没有给它一个值。创建子进程时,不会复制shell变量并将其提供给子进程。另一方面,环境变量被复制成为子进程的环境。
为了演示进程及其环境的另一个有趣特性,让我们在这个shell实例中定义 foo
:
xxxxxxxxxx
[me@linuxbox ~]$ foo="barbar"
接下来,我们将退出这个bash实例并返回到父实例,该父实例一直在耐心地等待子进程终止,然后再继续进行,就像它处理我们没有放在后台的任何其他程序一样。
xxxxxxxxxx
[me@linuxbox ~]$ exit
[me@linuxbox ~]$
我们将再次运行 ps
,看看我们是否已经返回到第一个实例。
xxxxxxxxxx
[me@linuxbox ~]$ ps
PID TTY TIME CMD
1011638 pts/9 00:00:00 bash
1014900 pts/9 00:00:00 ps
现在让我们看看这个例子中 foo
的值。
xxxxxxxxxx
[me@linuxbox ~]$ echo $foo
bar
[me@linuxbox ~]$
我们看到它仍然包含我们给它的值,而不是我们在子实例中设置的新值。这显示了关于子进程的一条重要规则:子进程不能改变其父进程的环境。当子进程终止时,它会带走它的环境。当我们开始编写shell脚本时,这一点将变得很重要。
shell提供的另一个方便的技巧是执行命令并为其提供临时环境变量的能力。有时我们想运行一个程序并给它一个特殊的环境值。一个很好的例子是 man
命令,它查找名为 MANWIDTH
的环境变量,该变量告诉 man
格式化其输出的宽度。例如,要让 man
将其输出格式化为最多75个字符宽(便于阅读的方便设置),我们可以这样做:
xxxxxxxxxx
[me@linuxbox ~]$ MANWIDTH=75 man ls
这将输出 ls
命令的手册页,并将其格式化为舒适的宽度。顺便说一句,别名是件好事:
xxxxxxxxxx
[me@linuxbox ~]$ alias man=’MANWIDTH=75 man’
现在,每当我们使用 man
命令时,它总是将行长度限制为75个字符。
由于我们知道启动文件的位置及其包含的内容,我们可以修改它们来定制我们的环境。
一般来说,要在 PATH
中添加目录或定义其他环境变量,请将这些更改放在 .bash_profile 中(或根据您的发行版的等效文件;例如,Ubuntu使用 .profile)。对于其他所有内容,请将更改放在 .bashrc 中。
注意:除非您是系统管理员,需要更改系统所有用户的默认设置,否则请将您的修改限制在主目录中的文件。当然可以更改 /etc 中的文件,如 profile ,在许多情况下这样做是明智的,但现在,让我们稳妥一点。
为了编辑(即修改)shell的启动文件以及系统上的大多数其他配置文件,我们使用了一个名为 text editor (文本编辑器)的程序。文本编辑器是一种程序,在某些方面类似于文字处理器,因为它允许我们用移动的光标编辑屏幕上的单词。它与文字处理器的不同之处在于,它只支持纯文本,并且通常包含为编写程序而设计的功能。文本编辑器是软件开发人员编写代码和系统管理员管理控制系统的配置文件的核心工具。
Linux上有很多不同的文本编辑器;大多数系统都安装了几个。为什么有这么多不同的?因为程序员喜欢编写它们,而且程序员广泛使用它们,所以他们编写编辑器来表达自己对如何工作的愿望。
文本编辑器分为两类:图形编辑器和文本编辑器。GNOME和KDE都包含一些流行的图形编辑器。GNOME附带了一个名为 gedit
的编辑器,通常在GNOME菜单中称为“文本编辑器”。KDE通常附带三个,它们是(按照复杂性的增加顺序) kedit
、 kwrite
和 kate
。
有许多基于文本的编辑器。我们经常遇到的流行的是 nano
、 vi
和 emacs
。 nano
编辑器是一个简单易用的编辑器,旨在替代PINE电子邮件套件提供的 pico
编辑器。 vi
编辑器(在大多数Linux系统上被一个名为 vim
的程序所取代,vim
是“vi improved”的缩写)是类Unix系统的传统编辑器。这将是我们下一章的主题。 emacs
编辑器最初由Richard Stallman编写。它是一个庞大的、通用的、无所不能的编程环境。虽然它很容易获得,但默认情况下很少安装在大多数Linux系统上。
通过在命令行中键入编辑器的名称,然后键入要编辑的文件的名称,可以调用文本编辑器。如果文件不存在,编辑器将假设我们要创建一个新文件。以下是一个使用 gedit
的示例:
xxxxxxxxxx
[me@linuxbox ~]$ gedit some_file
此命令将启动 gedit 文本编辑器,并加载名为“some_file”的文件(如果存在)。
图形文本编辑器非常直观,所以我们在这里不会介绍它们。相反,我们将专注于我们的第一个基于文本的文本编辑器 nano
。让我们启动 nano
并编辑 .bashrc 文件。但在我们这样做之前,让我们练习一些“安全计算”。每当我们编辑一个重要的配置文件时,最好先创建一个文件的备份副本。这可以保护我们,以防我们在编辑时弄乱文件。要创建 .bashrc 文件的备份,请执行以下操作:
xxxxxxxxxx
[me@linuxbox ~]$ cp .bashrc .bashrc.bak
如何称呼备份文件并不重要;请选择一个易于理解的名称。扩展名 .bak 、.sav 、.old 和 .orig 都是表示备份文件的流行方式。哦,记住 cp
会自动覆盖现有文件。
现在我们有了一个备份文件,我们将启动编辑器。
xxxxxxxxxx
[me@linuxbox ~]$ nano .bashrc
一旦 nano
启动,我们将得到这样的屏幕:
xxxxxxxxxx
GNU nano 2.0.3 File: .bashrc
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# User specific aliases and functions
[ Read 8 lines ]
^G Get Help ^O WriteOut ^R Read Fil ^Y Prev Pag ^K Cut Text ^C Cur Pos
^X Exit ^J Justify ^W Where Is ^V Next Pag ^U UnCut Te ^T To Spell
注意:如果您的系统没有安装nano,您可以使用图形编辑器。
屏幕由顶部的标题、中间正在编辑的文件文本和底部的命令菜单组成。由于 nano
旨在取代电子邮件客户端提供的文本编辑器,因此它的编辑功能相当不足。
在任何文本编辑器中,我们应该学习的第一个命令是如何退出程序。对于 nano
,我们按 Ctrl-x 退出。这在屏幕底部的菜单中显示。符号 ^X
表示 Ctrl-X 。这是许多程序使用的控制字符的常用符号。
我们需要知道的第二个命令是如何保存我们的工作。使用 nano
时,它是 Ctrl-o 。有了这些知识,我们就可以进行一些编辑了。使用向下箭头键和/或 PageDown 键,将光标移动到文件末尾,然后将以下行添加到 .bashrc 文件中:
xxxxxxxxxx
umask 0002
export HISTCONTROL=ignoredups
export HISTSIZE=1000
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
注意:您的发行版可能已经包含其中的一些,但重复不会造成任何损害。
下表详细说明了我们添加的含义:
行 | 含义 |
---|---|
umask 0002 | 设置umask以解决我们在第9章“权限”中讨论的共享目录的问题 |
export HISTCONTROL=ignoredups | 如果刚刚录制了同一命令,则使shell的历史记录功能忽略该命令。 |
export HISTSIZE=1000 | 将命令历史记录的大小从通常的默认值500行增加到1000行。 |
alias l.='ls -d .* --color=auto' | 创建一个名为 l. 的新命令,该命令显示所有以点开头的目录条目。 |
alias ll='ls -l --color=auto' | 创建一个名为 ll 的新命令,该命令显示一个长格式目录列表。 |
正如我们所看到的,我们的许多添加内容并不直观,因此最好在 .bashrc 文件中添加一些注释,以帮助向人类解释事情。使用编辑器,更改我们的添加内容,使其看起来像这样:
xxxxxxxxxx
# Change umask to make directory sharing easier
umask 0002
# Ignore duplicates in command history and increase
# history size to 1000 lines
export HISTCONTROL=ignoredups
export HISTSIZE=1000
# Add some helpful aliases
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
啊,好多了!更改完成后,按 Ctrl-o 保存修改后的 .bashrc 文件,然后按 Ctrl-x 退出 nano
。
每当您修改配置文件时,最好添加一些注释来记录您的更改。当然,明天你可能会记得你改变了什么,但六个月后呢?帮自己一个忙,添加一些评论。当你这样做的时候,记录下你所做的更改并不是一个坏主意。
Shell脚本和bash启动文件使用 #
符号开始注释。其他配置文件可能使用其他符号。大多数配置文件都会有注释。把它们当作指南。
您经常会在配置文件中看到注释掉的行,以防止受影响的程序使用它们。这样做是为了给读者提供可能的配置选择或正确配置语法示例的建议。例如,Ubuntu 18.04的.bashrc文件包含以下行:
xxxxxxxxxx
# some more ls aliases
#alias ll='ls -l'
#alias la='ls -A'
#alias l='ls -CF'
最后三行是已注释掉的有效别名定义。如果你从这三行中删除前导的 #
符号,这是一种称为取消注释(uncommenting)的技术,你将激活别名。相反,如果在行首添加 #
符号,则可以停用配置行,同时保留其中包含的信息。
我们对 .bashrc 所做的更改只有在关闭终端会话并启动新会话后才会生效,因为 .bashrc 文件仅在会话开始时读取。但是,我们可以使用以下命令强制bash重新读取修改后的 .bashrc 文件:
xxxxxxxxxx
[me@linuxbox ~]$ source ~/.bashrc
这样做之后,我们应该能够看到我们的变化的效果。尝试一个新别名。
xxxxxxxxxx
[me@linuxbox ~]$ ll
source
命令(可以缩写为 .
)是一个内置的shell,它将文件直接读取到当前shell中,就像在键盘上输入其内容一样。是的,我们在shell启动文件中看到的所有奇怪的东西都是shell可以理解并采取行动的东西。许多旧的基于文本的操作系统(DOS、CP/M等)主要作为简单的程序启动器运行。正如我们所见,Unix风格的shell当然可以做到这一点,但它们也可以做得更多。..
在本章中,我们学习了一项基本技能——使用文本编辑器编辑配置文件。接下来,当我们阅读命令手册页时,请注意命令支持的环境变量。可能有一两颗宝石。在后面的章节中,我们将学习shell函数,这是一个强大的功能,您还可以将其包含在bash启动文件中,以添加到您的自定义命令库中。