7.2. 您的文本环境

7.2.1. 环境变量

7.2.1.1. 概述

我们已经提到过几个环境变量,例如PATHHOME。到现在为止,我们只看到了它们为 shell 服务的一些例子。但是,还有许多其他的 Linux 实用程序需要关于您的信息才能很好地工作。

除了路径和主目录之外,程序还需要哪些其他信息?

很多程序都想知道您正在使用的终端类型;此信息存储在TERM变量中。在文本模式下,这将是 *linux* 终端仿真,在图形模式下,您可能会使用 *xterm*。很多程序都想知道您最喜欢的编辑器是什么,以防它们必须在子进程中启动编辑器。您正在使用的 shell 存储在SHELL变量中,操作系统类型存储在OS等等。可以通过输入 printenv 命令来查看当前为您的会话定义的所有变量的列表。

环境变量由 shell 管理。与常规 shell 变量相反,环境变量由您启动的任何程序继承,包括另一个 shell。新的进程会被分配这些变量的副本,它们可以读取、修改这些变量,并依次传递给它们自己的子进程。

变量名没有什么特别之处,除了约定俗成的常用变量都使用大写字母。您可以随意起任何名字,尽管有一些标准变量非常重要,以至于在每个 Linux 系统上都是相同的,例如PATHHOME.

7.2.1.2. 导出变量

通常使用 echo 命令显示单个变量的内容,如下面的例子所示

debby:~> echo $PATH
/usr/bin:/usr/sbin:/bin:/sbin:/usr/X11R6/bin:/usr/local/bin

debby:~> echo $MANPATH
/usr/man:/usr/share/man/:/usr/local/man:/usr/X11R6/man

如果您想更改变量的内容,以便对其他程序有用,则必须将新值从您的环境中导出到运行这些程序的环境中。一个常见的例子是导出PATH变量。您可以声明它如下,以便能够玩位于/opt/FlightGear/bin:

debby:~> PATH=$PATH:/opt/FlightGear/bin

中的飞行模拟器软件。这指示 shell 不仅在当前路径$PATH/opt/FlightGear/bin.

中搜索程序,而且还在附加目录PATH中搜索。

debby:~> runfgfs
bash: runfgfs: command not found

但是,只要环境变量不知道

debby:~> export PATH

debby:~> runfgfs
--flight simulator starts--

的新值,事情仍然行不通。

导出变量是使用 shell 内置命令 export 完成的。Bash 中,我们通常通过一个优雅的步骤来完成此操作=export

VARIABLEvalue相同的技术也用于valueMANPATH

debby:~> export MANPATH=$MANPATH:/opt/FlightGear/man

debby:~> echo $MANPATH
/usr/man:/usr/share/man:/usr/local/man:/usr/X11R6/man:/opt/FlightGear/man

变量,它告诉 man 命令在哪里查找压缩的 man 页面。如果新的软件被添加到系统中的新的或不常见的目录中,那么它的文档可能也会在一个不常见的目录中。如果您想阅读新软件的 man 页面,请扩展

变量。

您可以通过将其添加到您的 shell 设置文件中来避免在您打开的每个窗口中重新键入此命令,请参阅 第 7.2.2 节

7.2.1.3. 保留变量

下表概述了最常见的预定义变量。表 7-1. 常用环境变量
变量名存储的信息
DISPLAYX Window 系统用于标识显示服务器
DOMAIN域名
EDITOR存储您最喜欢的行编辑器
HOMEHISTSIZE
shell 历史文件的大小,以行数计指向您主目录的路径
HOSTNAME本地主机名
INPUTRC输入设备(如键盘)的定义文件的位置
LANG首选语言
LD_LIBRARY_PATH搜索库的路径
LOGNAME登录名
valueMAIL
OS您的传入邮件文件夹的位置
MANPATH搜索 man 页面的路径
OS描述操作系统的字符串
PATHOSTYPE
有关版本等的更多信息PAGER
man 等程序使用,这些程序需要知道在输出超过一个终端窗口的情况下该怎么做。PATH
命令的搜索路径PS1
SHELL主提示符
TERMPS2
二级提示符PWD
当前工作目录SHELL
当前 shellTERM
终端类型UID
用户 IDUSER(NAME)

用户名

VISUAL

您最喜欢的全屏编辑器XENVIRONMENT您的 X 行为的个人设置的位置XFILESEARCHPATH.

搜索图形库的路径许多变量不仅是预定义的,而且是预先设置的,使用配置文件。我们将在下一节讨论这些内容。7.2.2. Shell 设置文件当输入 ls -al 命令以获得所有文件的长列表,包括以点开头的文件时,在您的主目录中,您将看到一个或多个以 . 开头并以 rc 结尾的文件。对于 bash 的情况,这是.bashrc。这是系统范围内的配置文件, /etc/bashrc的对应物。当登录到交互式登录 shell 时,login 将进行身份验证、设置环境并启动您的 shell。对于 bash,下一步是读取通用的XFILESEARCHPATHprofile

/etc中读取,如果该文件存在。bash 然后查找

~/.bash_profile

~/.bash_login

~/.profile

,按顺序,并从第一个存在且可读的文件中读取和执行命令。如果都不存在,则应用当登录 shell 退出时,bash 读取并执行文件PATH, ~/.bash_logoutshell 历史文件的大小,以行数计中的命令(如果该文件存在)。

debby:~> cat /etc/profile
# /etc/profile

# System wide environment and startup programs, for login setup
# Functions and aliases go in /etc/bashrc


# Path manipulation
if [ `id -u` = 0 ] && ! echo $PATH | /bin/grep -q "/sbin" ; then
    PATH=/sbin:$PATH
fi

if [ `id -u` = 0 ] && ! echo $PATH | /bin/grep -q "/usr/sbin" ; then
    PATH=/usr/sbin:$PATH
fi

if [ `id -u` = 0 ] && ! echo $PATH | /bin/grep -q "/usr/local/sbin"
    then
    PATH=/usr/local/sbin:$PATH
fi

if ! echo $PATH | /bin/grep -q "/usr/X11R6/bin" ; then
    PATH="$PATH:/usr/X11R6/bin"
fi

此过程在 loginbash 手册页中详细说明。7.2.3. 一组典型的设置文件, 7.2.3.1. /etc/profile 示例让我们看看这些配置文件中的一些。首先读取/etc/profile,其中设置了重要的变量,如USER

# No core files by default
ulimit -S -c 0 > /dev/null 2>&1

这些行检查要设置的路径:如果 *root* 打开一个 shell(用户 ID 为 0),则检查/sbin/usr/sbin

USER=`id -un`
LOGNAME=$USER
MAIL="/var/spool/mail/$USER"

HOSTNAME=`/bin/hostname`
HISTSIZE=1000

/usr/local/sbin

if [ -z "$INPUTRC" -a ! -f "$HOME/.inputrc" ]; then
    INPUTRC=/etc/inputrc
fi

是否在路径中。如果不在,则添加它们。对于每个人,都会检查HOSTNAME/usr/X11R6/bin是否在路径中。所有垃圾都进入

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC

/dev/null

,如果用户不更改此设置。

for i in /etc/profile.d/*.sh ; do
    if [ -r $i ]; then
    	. $i
    fi
done
unset i

这里,一般变量被分配了它们适当的值。如果变量未设置,并且用户的家目录中没有.inputrc,则加载默认的输入控制文件。

所有变量都被导出,以便它们可用于请求有关您的环境信息的其他程序。

7.2.3.2. profile.d 目录读取并执行来自/etc/profile.d

debby:~> cat .bash_profile 
#################################################################
#                                                               #
#   .bash_profile file                                          #
#                                                               #
#   Executed from the bash shell when you log in.               #
#                                                               #
#################################################################

source ~/.bashrc
source ~/.bash_login

目录的所有可读 shell 脚本。这些脚本执行诸如启用 *color-ls*,将 vi 别名为 vim,设置区域设置等操作。临时变量i被取消设置,以防止它干扰以后的 shell 行为。/etc/bashrc7.2.3.3. .bash_profile 示例

然后,bash 在用户的家目录中查找

.bash_profile/etc/bashrc这个非常简单的文件指示您的 shell 首先读取i~/.bashrcXFILESEARCHPATH然后读取有关版本等的更多信息。当您在 shell 环境中工作时,您会经常遇到 source 内置 shell 命令:它用于将配置更改应用于当前环境。i7.2.3.4. .bash_login 示例XFILESEARCHPATH

文件通过设置 umask 值来定义默认的文件保护,请参阅 第 3.4.2.2 节。该

文件用于定义一堆特定于用户的别名和函数以及个人环境变量。它首先读取XFILESEARCHPATH,它描述了默认提示符 (

debby:~> cat /etc/bashrc
# /etc/bashrc

# System wide functions and aliases
# Environment stuff goes in /etc/profile

# by default, we want this to get set.
# Even for non-interactive, non-login shells.
if [ `id -gn` = `id -un` -a `id -u` -gt 99 ]; then
	umask 002
else
	umask 022
fi

) 和默认的 umask 值。之后,您可以添加您自己的设置。如果不存在

# are we an interactive shell?
if [ "$PS1" ]; then
  if [ -x /usr/bin/tput ]; then
    if [ "x`tput kbs`" != "x" ]; then 
# We can't do this with "dumb" terminal
      stty erase `tput kbs`
    elif [ -x /usr/bin/wc ]; then
      if [ "`tput kbs|wc -c `" -gt 0 ]; then 
# We can't do this with "dumb" terminal
        stty erase `tput kbs`
      fi
    fi
  fi
  case $TERM in
	xterm*)
	if [ -e /etc/sysconfig/bash-prompt-xterm ]; then
		PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
	else
   PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME%%.*}:\
${PWD/$HOME/~}\007"'
	fi
    ;;
	*)
   [ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=\
/etc/sysconfig/bash-prompt-default
	    ;;
    esac
    [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
    
    if [ "x$SHLVL" != "x1" ]; then # We're not a login shell
        for i in /etc/profile.d/*.sh; do
	    if [ -x $i ]; then
	        . $i
	    fi
	done
    fi
fi

,则默认读取

/etc7.2.3.5. /etc/bashrc 示例

debby:~> cat .bash_logout
# ~/.bash_logout

clear

您的

文件可能看起来像这样

这些行设置 umask 值。然后,根据 shell 的类型,设置提示符。

7.2.3.6. .bash_logout 示例

注销时,执行

[jerry@nowhere jerry]$ MYPROMPT=$PS1

[jerry@nowhere jerry]$ echo $MYPROMPT
[\u@\h \W]\$

[jerry@nowhere jerry]$

中的命令,例如,可以清除终端,以便您在注销远程会话或离开系统控制台时拥有一个干净的窗口。

让我们在下一节中仔细看看这些脚本是如何工作的。请随时参考 info bash

7.2.4. Bash 提示符

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息=7.2.4.1. 简介

    Bash 提示符可以做的不仅仅是显示诸如您的用户名、您的机器名称以及关于当前工作目录的一些指示之类的简单信息。我们可以添加其他信息,例如当前日期和时间、已连接的用户数等。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息=但是,在我们开始之前,我们将把我们当前的提示符保存在另一个环境变量中

    显示日期、用户名、主机名和当前工作目录。注意 \W 仅显示当前工作目录的基本名称。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="{\!} "

    显示每个命令的历史记录编号。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="\[\033[1;35m\]\u@\h\[\033[0m\] "

    以粉色显示 user@host。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="\[\033[1;35m\]\u\[\033[0m\] \[\033[1;34m\]\w\[\033[0m\] "

    将用户名设置为粉色,当前工作目录设置为蓝色。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="\[\033[1;44m\]$USER is in \w\[\033[0m\] "

    为那些难以区分提示符和他们输入内容的人提供提示。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="\[\033[4;34m\]\u@\h \w \[\033[0m\]"

    带下划线的提示符。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="\[\033[7;34m\]\u@\h \w \[\033[0m\] "

    蓝色背景上的白色字符。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息="\[\033[3;35m\]\u@\h \w \[\033[0m\]\a"

    浅色字体的粉色提示符,在您的命令完成时提醒您。

  • 导出变量是使用 shell 内置命令 export 完成的。有关版本等的更多信息=...

变量被导出,以便后续执行的命令也能知道环境变量。您想要的提示符配置行最好放在您的 shell 配置文件中。i.

如果您愿意,提示符可以执行 shell 脚本,并在不同条件下表现不同。您甚至可以在每次发出命令时让提示符播放音乐,尽管这很快就会变得无聊。更多信息可以在 Bash-Prompt HOWTO 中找到。

7.2.5. Shell 脚本

7.2.5.1. 什么是脚本?

正如我们在 shell 配置示例中看到的,shell 脚本是一个包含 shell 命令的文本文件。当这样的文件作为调用 Bash 的第一个非选项参数使用时,并且既没有-c也没有-s选项被提供,Bash 会读取和执行文件中的命令,然后退出。这种操作模式创建了一个非交互式 shell。当 Bash 运行 shell 脚本时,它会将特殊参数0设置为文件的名称,而不是 shell 的名称,并且位置参数(脚本名称后面的所有内容)设置为剩余的参数(如果存在)。如果没有提供其他参数,则位置参数将被取消设置。

可以使用 chmod 命令打开执行位,使 shell 脚本可执行。当 Bash 在搜索PATH命令时找到这样一个文件,它会生成一个子 shell 来执行它。换句话说,执行

filenameARGUMENTS

等同于执行

bashfilename ARGUMENTS

如果 "filename" 是一个可执行的 shell 脚本。这个子 shell 会重新初始化自身,因此效果就像调用了一个新的 shell 来解释脚本一样,但父进程记住的命令位置(参见 Info 页面中的 hash)会被子进程保留。

大多数 UNIX 版本将此作为操作系统命令执行机制的一部分。如果脚本的第一行以两个字符 "#!" 开头,则该行剩余部分指定程序的解释器。因此,您可以指定 bashawkperl 或其他解释器或 shell,并用该语言编写脚本文件的其余部分。

解释器的参数包括脚本文件第一行解释器名称后的单个可选参数,后跟脚本文件的名称,后跟其余参数。Bash 将在不自行处理它的操作系统上执行此操作。

Bash 脚本通常以

#! /bin/bash

(假设 Bash 已安装在/bin) 开头,因为这确保了 Bash 将被用来解释脚本,即使它是在另一个 shell 下执行的。

7.2.5.2. 一些简单的例子

一个非常简单的脚本,只包含一个命令,向执行它的用户打招呼

[jerry@nowhere ~] cat hello.sh
#!/bin/bash
echo "Hello $USER"

该脚本实际上只包含一个命令 echo,它使用 ($) 的,即~/.bash_logout环境变量来打印一个根据发出命令的用户定制的字符串。

另一个单行命令,用于显示已连接的用户

#!/bin/bash
who | cut -d " " -f 1 | sort -u

这是一个包含更多行的脚本,我用它来制作目录中所有文件的备份副本。该脚本首先列出当前目录中的所有文件,并将其放入变量LIST中。然后,它为每个文件设置副本的名称,然后复制文件。对于每个文件,都会打印一条消息

tille:~> cat bin/makebackupfiles.sh
#!/bin/bash
# make copies of all files in a directory
LIST=`ls`
for i in $LIST; do
	ORIG=$i
	DEST=$i.old
	cp $ORIG $DEST
	echo "copied $i"
done

仅仅输入像 mv * *.old 这样的行是行不通的,当您在一组测试文件上尝试时,您会注意到这一点。添加了一个 echo 命令以显示一些活动。echo 在脚本无法工作时通常很有用:在每个可疑步骤后插入一个,您很快就会找到错误。

.bash_profile/etc/rc.d/init.d目录包含大量示例。让我们看看这个控制虚构的 ICanSeeYou 服务器的脚本

#!/bin/sh
# description: ICanSeeYou allows you to see networked people

# process name: ICanSeeYou
# pidfile: /var/run/ICanSeeYou/ICanSeeYou.pid
# config: /etc/ICanSeeYou.cfg

# Source function library.
. /etc/rc.d/init.d/functions

# See how (with which arguments) we were called.
case "$1" in
	start)
		echo -n "Starting ICanSeeYou: "
		daemon ICanSeeYou
		echo
		touch /var/lock/subsys/ICanSeeYou
		;;
	stop)
		echo -n "Shutting down ICanSeeYou: "
		killproc ICanSeeYou
		echo
		rm -f /var/lock/subsys/ICanSeeYou
		rm -f /var/run/ICanSeeYou/ICanSeeYou.pid
		;;
	status)
		status ICanSeeYou
		;;
	restart)
		$0 stop
		$0 start
		;;
	*)
		echo "Usage: $0 {start|stop|restart|status}"
		exit 1
esac

exit 0

首先,使用 . 命令(点)加载一组 shell 函数,几乎所有 shell 脚本都在/etc/rc.d/init.d中使用。 然后发出一个 case 命令,该命令定义了脚本可以执行的 4 种不同方式。一个例子可能是 ICanSeeYou start。要应用哪个 case 的决定是通过读取脚本的(第一个)参数(使用表达式 $1)来做出的。

当没有给出符合要求的输入时,会应用默认 case,用星号标记,脚本会给出错误消息。 case 列表以 esac 语句结束。在 start case 中,服务器程序作为守护程序启动,并分配一个进程 ID 和锁。在 stop case 中,跟踪并停止服务器进程,并删除锁和 PID。诸如daemon选项和诸如killproc之类的函数在/etc/rc.d/init.d/functions文件中定义。此设置特定于此示例中使用的发行版。您系统上的 init 脚本可能会使用其他文件中定义的其他函数,或者根本不使用任何函数。

成功后,脚本向其父进程返回退出代码零。

这个脚本是使用函数的一个很好的例子,这使得脚本更容易阅读,并且工作完成得更快。请注意,他们使用 sh 而不是 bash,以使其在更广泛的系统上可用。在 Linux 系统上,将 bash 作为 sh 调用会导致 shell 以符合 POSIX 的模式运行。

bash 手册页包含有关组合命令、for- 和 while-循环以及正则表达式的更多信息,以及示例。来自与本 Linux 入门指南的同一作者编写的针对系统管理员和高级用户的可理解的 Bash 课程(带有练习)位于 http://tille.garrels.be/training/bash/Bash 功能和应用的详细描述位于参考指南 Advanced Bash Scripting 中。