Linux命令行与shell编程 第5章 使用Linux环境变量

本章内容

  • 什么是环境变量
  • 设置环境变量
  • 删除环境变量
  • 默认shell环境变量
  • 定位系统环境变量
  • 可变数组
  • 使用命令别名

Linux 环境变量能帮你提升Linux shell体验。但以新手来说,这是一个容易感到困惑的话题。

存储在哪里,怎样使用,以及怎样创建自己的环境变量。

5.1 什么是环境变量

bash shell 用一个称作为环境变量(environment variables)的特性来存储有关shell会话和工作环境。,在内存

中存储,以便运行在shell上的程序和脚本访问。这些数据可可是用来识别用户账户、系统、shell的特性以及任何

其他你需要存储的数据。

在bash shell中,环境变量分为两类"

  • 全局变量
  • 局部变量

5.1.1 全局环境变量

全局环境变量不公对shell会话可见,对所有shell创建的子进程也可见。局部变量则只对创建

它们的shell可见。这让全局环境变量对那些子进程中需要获得父进程信息的程序来说非常有用。

Linux系统在你开始bash会话之前就设置了一些全局环境变量。系统环境变量一律使用全大写字母

以区别于变通用户的环境变量。

查看全局变量:

[jingpan@localhost ~]$ printenv
HOSTNAME=localhost.localdomain
TERM=linux
SHELL=/bin/bash
HISTSIZE=1000
SSH_CLIENT=192.168.1.64 60164 22
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
SSH_TTY=/dev/pts/0
USER=jingpan
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:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.tbz=01;31:*.tbz2=01;31:*.bz=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/jingpan
PATH=/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/jingpan/bin
PWD=/home/jingpan
LANG=zh_CN.UTF-8
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
HISTCONTROL=ignoredups
SHLVL=1
HOME=/home/jingpan
LOGNAME=jingpan
QTLIB=/usr/lib64/qt-3.3/lib
CVS_RSH=ssh
SSH_CONNECTION=192.168.1.64 60164 192.168.1.3 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
G_BROKEN_FILENAMES=1
_=/usr/bin/printenv
[jingpan@localhost ~]$ 
如你所见,系统bash shell设置了很多全局环境变量。它们中的大部分都是系统在用户登录系统时设置的。


显示单个环境变量的值:

[jingpan@localhost ~]$ echo $HOME
/home/jingpan
[jingpan@localhost ~]$ 

正如前面提到的,全局环境变量在当前shell会话的子进程中也是可见的:

[jingpan@localhost ~]$ bash
[jingpan@localhost ~]$ echo $HOME
/home/jingpan

在这个例子中,用bash命令启动一个新和shell后,显示了HOME环境变量的当月前值,这个值是你登录进主shell时设定的。

当然,这个值在子shell进程中依然是存在的。

5.1.2 局部环境变量

局部环境变量只能在定义它们的进程中可见。

set命令会显示某个特定进程设置的所有环境变量。当然,这也包括全局环境变量。

下面是用来示例的set命令的输出:

[jingpan@localhost ~]$ set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
BASH_VERSINFO=([0]="4" [1]="1" [2]="2" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")
BASH_VERSION='4.1.2(2)-release'
COLORS=/etc/DIR_COLORS
COLUMNS=96
CVS_RSH=ssh
DIRSTACK=()
EUID=500
GROUPS=()
G_BROKEN_FILENAMES=1
HISTCONTROL=ignoredups
HISTFILE=/home/jingpan/.bash_history
HISTFILESIZE=1000
HISTSIZE=1000
HOME=/home/jingpan
HOSTNAME=localhost.localdomain
HOSTTYPE=x86_64
ID=500
IFS=$' \t\n'
LANG=zh_CN.UTF-8
LESSOPEN='||/usr/bin/lesspipe.sh %s'
LINES=40
LOGNAME=jingpan
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:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.tbz=01;31:*.tbz2=01;31:*.bz=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:'
MACHTYPE=x86_64-redhat-linux-gnu
MAIL=/var/spool/mail/jingpan
MAILCHECK=60
OPTERR=1
OPTIND=1
OSTYPE=linux-gnu
PATH=/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/jingpan/bin
PIPESTATUS=([0]="0")
PPID=4205
PS1='[\u@\h \W]\$ '
PS2='> '
PS4='+ '
PWD=/home/jingpan
QTDIR=/usr/lib64/qt-3.3
QTINC=/usr/lib64/qt-3.3/include
QTLIB=/usr/lib64/qt-3.3/lib
SHELL=/bin/bash
SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
SHLVL=2
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SSH_CLIENT='192.168.1.64 60164 22'
SSH_CONNECTION='192.168.1.64 60164 192.168.1.3 22'
SSH_TTY=/dev/pts/0
TERM=linux
UID=500
USER=jingpan
_=/home/jingpan
colors=/etc/DIR_COLORS
__udisks () 
{ 
    local IFS='
';
    local cur="${COMP_WORDS[COMP_CWORD]}";
    if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--show-info" ]; then
        COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
    else
        if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--inhibit-polling" ]; then
            COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
        else
            if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--mount" ]; then
                COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
            else
                if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--unmount" ]; then
                    COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
                else
                    if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--detach" ]; then
                        COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
                    else
                        if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--ata-smart-refresh" ]; then
                            COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
                        else
                            if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--ata-smart-simulate" ]; then
                                _filedir || return 0;
                            else
                                if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--set-spindown" ]; then
                                    COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
                                else
                                    if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--poll-for-media" ]; then
                                        COMPREPLY=($(compgen -W "$(udisks --enumerate-device-files)" -- $cur));
                                    else
                                        COMPREPLY=($(IFS=: compgen -W "--dump:--inhibit-polling:--inhibit-all-polling:--enumerate:--enumerate-device-files:--monitor:--monitor-detail:--show-info:--help:--mount:--mount-fstype:--mount-options:--unmount:--unmount-options:--detach:--detach-options:--ata-smart-refresh:--ata-smart-wakeup:--ata-smart-simulate:--set-spindown:--set-spindown-all:--spindown-timeout:--poll-for-media" -- $cur));
                                    fi;
                                fi;
                            fi;
                        fi;
                    fi;
                fi;
            fi;
        fi;
    fi
}
[jingpan@localhost ~]$ 

可以看到,所有通过printenv命令看到的全局环境变量都出现在了set命令的输出中。但

在set命令的输出中还有一些其他的环境变量,这些就是局部环境变量。

5.2.1 设置局部环境变量

一旦启动了bash shell(或者执行一个shell脚本),你就能创建在这个shell进程内可见的局部

变量了。你可以通过等号来给环境变量赋值、值可以是数值或字符串:

[jingpan@localhost ~]$ test=testing
[jingpan@localhost ~]$ echo $test
testing

如果要给变量赋一个含有空格的字符串值,必须用单引号来界定字符串的开始和末尾:

[jingpan@localhost ~]$ test=testing a long string
bash: a: command not found
[jingpan@localhost ~]$ test='testing a long string'
[jingpan@localhost ~]$ echo $test
testing a long string
[jingpan@localhost ~]$ 

没有单引号的话,bash shell会以为下个字符串是另一个要执行的命令。注意,你定义的局部环境

变量用的是小写字母,而到目前为止你所看到的系统环境变量都是用大写字母。

的环境变量名称、等号和值之间没有空格。

[jingpan@localhost /]$ test2 = test
bash: test2: command not found
[jingpan@localhost /]$ 

设置了局部环境变量后,就能在shell进程的任何地方使用它了。但是,如果创建了另外一个shell,它在子shell

中就不可用了:

[jingpan@localhost /]$ bash
[jingpan@localhost /]$ echo $test

[jingpan@localhost /]$ exit
exit
[jingpan@localhost /]$ echo $test
testing a long string 

在这个例子中,你启动了一个子shell。如你所见,test环境变量在子shell中并不可见(它含的是个宿舍)。当

你退出子shell回到原来的shell时,局部环境变量依然在。

类似地,如果你在子进程中设置了一个局部变量,一旦你退出了子进程,寻个局部环境变量就不能用了。

[jingpan@localhost /]$ bash
[jingpan@localhost /]$ test2=test2ing
[jingpan@localhost /]$ echo $test2
test2ing
[jingpan@localhost /]$ exit
exit
[jingpan@localhost /]$ echo $test2

当我们回到父shell时,子shell中设置的test环境变量就不存在了。

5.2.2 设置全局环境变量

创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局环境中。

这个过程通过export命令来完成.

[jingpan@localhost /]$ test='testing a long string'
[jingpan@localhost /]$ echo $test
testing a long string
[jingpan@localhost /]$ export test
[jingpan@localhost /]$ bash
[jingpan@localhost /]$ echo $test
testing a long string
[jingpan@localhost /]$ 

导出局部环境变量test后,我们启动了子shell进程并在子shell中查看了test环境变量的值。

这次,因为export命令让它变成了全局的,环境变量保持了它的值。

5.3 删除环境变量

既然要以创建一个新环境变量,删除一个已经存在的环境变量也就有用了。你可以用unset命令来删除环境变量。

[jingpan@localhost ~]$ test=testing
[jingpan@localhost ~]$ echo $test
testing
[jingpan@localhost ~]$ unset test
[jingpan@localhost ~]$ echo $test

在unset命令中引用环境变量时,记住不要用美元符($)。

5.4 默认shelll环境变量

默认情况下bash shell会用一些特定的环境变量来定义系统环境。你可以一直使用Linux系统上默认定义的这些变量。

HOME                 当前用户的主目录

PATH                    冒号分隔的shell查找命令的目录列表

在shell命令行界面(Command Line Interface,CLI)输入命令时,shell必须在系统中查找程序。在我的Linux系统上,PATH环境

变量是这样的:

[jingpan@localhost ~]$ echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/jingpan/bin

这说明shell将在8个目录查找命令。PATH中的每个目录都由冒号分隔。在PATH变量的末尾没什么特殊符号

来说明这是目录列表的结尾。你强以通过在PATH变量的末尾加个冒号再加个新的目录来添加其他目录。

PATH变量同时显示了shell查找命令的顺序。

设置PATH环境变量

PATH环境变量是Linux系统上造成最多问题的变量。它定义了命令行输入命令的搜索路径。

如果找不到命令,它会产生一个错误:

[root@localhost linux]# myprog
bash: myprog: command not found
[root@localhost linux]# 

问题是通常应用会把可执行程序放到不在PATH环境变量中的目录。解决的办法是保证PATH环境变量包含了

所有存放应用的目录。

你可以添加新的搜索目录到现有的PATH环境变量,无需从头定义。PATH中的目录之间是用冒号分隔的,所以你

只需要引用原来的PATH值,然后再给字符串添加新目录。可以参考下面的例子:

[root@localhost linux]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/jingpan/bin
[root@localhost linux]# PATH=$PATH:/home/jingpan/linux
[root@localhost linux]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/jingpan/bin:/home/jingpan/linux
[root@localhost linux]# myprog 
sum = 210
[root@localhost linux]# 

将目录加到PATH环境变量之后,现在可以在虚拟目录结构的任何位置执行程序了:

[root@localhost linux]# cd /etc
[root@localhost etc]# myprog 
sum = 210
[root@localhost etc]# 

程序员通常用的办法是将单点符也加到PATH环境变量里。这个单点符代表当前目录

5.6 定位系统环境变量

Linux系统用环境变量来在程序和脚本中标识它自己。这为你的程序提供了获得系统信息的一个简便方法。

问题是如何设置这些变量。

在你登录Linux系统启动一个bash shell时,默认情况下bash在几个文件中查找命令。这些文件称作启动文件。

bash检查的启动文件取决于你启动bash  shell的方式。启动bash shell有3种方式:

  •     登录时当做默认登录shell
  •     作为非登录shell的交互式shell
  •     作为运行脚本的非交互shell

5.6.1 登录shell

    当你登录Linux系统时,bash shell 会作为登录shell启动。登录shell会从4个不同的启动文件里

读取命令。下面是bash shell 处理这些文件的次序:

  • /etc/profile;
  • /$HOME/.bash_profile;
  • /$HOME/.bash_login;
  • /$HOME/.profile

1. /etc/profile文件

/etc/profile文件是bash shell的主启动文件。只要你登录了Linux系统,bash就会执行/etc/profile

文件中的命令。不同的Linux发行版在这个文件里放了不同的命令。在这个Linux系统上,它看起来是这样的:

[jingpan@localhost ~]$ cat  /etc/profile
# /etc/profile

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

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

pathmunge () {
    case ":${PATH}:" in
        *:"$1":*)
            ;;
        *)
            if [ "$2" = "after" ] ; then
                PATH=$PATH:$1
            else
                PATH=$1:$PATH
            fi
    esac
}


if [ -x /usr/bin/id ]; then
    if [ -z "$EUID" ]; then
        # ksh workaround
        EUID=`/usr/bin/id -u`
        UID=`/usr/bin/id -ru`
    fi
    USER="`/usr/bin/id -un`"
    LOGNAME=$USER
    MAIL="/var/spool/mail/$USER"
fi

# Path manipulation
if [ "$EUID" = "0" ]; then
    pathmunge /sbin
    pathmunge /usr/sbin
    pathmunge /usr/local/sbin
else
    pathmunge /usr/local/sbin after
    pathmunge /usr/sbin after
    pathmunge /sbin after
fi

HOSTNAME=`/bin/hostname 2>/dev/null`
HISTSIZE=1000
if [ "$HISTCONTROL" = "ignorespace" ] ; then
    export HISTCONTROL=ignoreboth
else
    export HISTCONTROL=ignoredups
fi

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

# By default, we want umask to get set. This sets it for login shell
# Current threshold for system reserved uid/gids is 200
# You could check uidgid reservation validity in
# /usr/share/doc/setup-*/uidgid file
if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
    umask 002
else
    umask 022
fi

for i in /etc/profile.d/*.sh ; do
    if [ -r "$i" ]; then
        if [ "${-#*i}" != "$-" ]; then
            . "$i"
        else
            . "$i" >/dev/null 2>&1
        fi
    fi
done

unset i
unset -f pathmunge
[jingpan@localhost ~]$ 

这个文件中你能看到的大部分命令和脚本都会在第10章具体讲到。现在重要是留意一下这个文件中设置的环境变量。看一下文件

底部的导出行:

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL

这保证了这些环境变量对这个登录shell创建的所有子进程都有效。

profile文件还有一个复杂的特性。它有个for语句,会逐一访问位于/etc/profile.d目录的每个文件

(for 语句我们会在第13章中讨论)。它为Linux系统提供了一个集中存放用户登录时要执行的应用专属的启动文件的地方。

在这个Linux系统上,profile.d目录下有如下文件:

[jingpan@localhost ~]$ ls -l /etc/profile.d
总用量 72
-rw-r--r--  1 root root 1150 2月   7 2017 colorls.csh
-rw-r--r--  1 root root 1172 2月   7 2017 colorls.sh
-rw-r--r--. 1 root root   92 11月 22 2013 cvs.csh
-rw-r--r--. 1 root root   78 11月 22 2013 cvs.sh
-rw-r--r--  1 root root  192 1月  25 2017 glib2.csh
-rw-r--r--  1 root root  192 1月  25 2017 glib2.sh
-rw-r--r--  1 root root   58 9月   1 2017 gnome-ssh-askpass.csh
-rw-r--r--  1 root root   70 9月   1 2017 gnome-ssh-askpass.sh
-rw-r--r--  1 root root 1745 10月  4 10:26 lang.csh
-rw-r--r--  1 root root 2706 10月  4 10:26 lang.sh
-rw-r--r--. 1 root root  123 6月   4 2014 less.csh
-rw-r--r--. 1 root root  121 6月   4 2014 less.sh
-rw-r--r--. 1 root root  976 9月  24 2011 qt.csh
-rw-r--r--. 1 root root  912 9月  24 2011 qt.sh
-rw-r--r--  1 root root 2142 3月  22 2017 udisks-bash-completion.sh
-rw-r--r--  1 root root  105 12月 22 2016 vim.csh
-rw-r--r--  1 root root  269 12月 22 2016 vim.sh
-rw-r--r--. 1 root root  169 5月  20 2009 which2.sh
[jingpan@localhost ~]$ 

不难发现,这些基本都跟系统上的特定应用有关。大部分应用会创建两个启动文件:一个给

bash shell(使用.sh扩展名),一个给c shell(使用.csh扩展名)。

lang.csh和lang.sh文件会尝试去判定系统上所采用的默认语言文字集,然后正确地设置LANG环境变量。

2. $HOME目录下的启动文件

剩下的3个启动文件都起着同一个作用:提供一个用户专属的启动文件来定义用户志有的环境变量。大多数Linux发

行版只用这3个启动文件中的一个:

  • $HOME/.bash_profile;
  • $HOME/.bash_login;;
  • $HOME/.profile;

注意这3个文件都以点开头,这说明它们是隐藏文件(不会在通常的ls命令输出列表中出现)。

它们在用户的HOME目录下,所以每个用户可以编辑这些文件并添加自己的环境变量来给

他们启动的每个bash shell会话用。

[jingpan@localhost ~]$ cat .bash_profile
# .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
[jingpan@localhost ~]$ 

.bash_profiel启动文件会检查HOME目录中是不是有另一个叫.bashrc的启动文件(我们将会在5.6.2节中介绍)。

如果有的话,启动文件会先执行它里面的命令。下一步,启动文件将一个目录加到了PATH环境变量,在HOME目录下

提供了一个放置可执行文件的通用位置。

5.6.2 交互式shell

如果你的bash shell不是登录系统时启动的(比如你在命令行提示符下敲入bash启动),你启动的

shell称作为交互式shell。 交互式shell不会像shell一样运行,但它依然提供了命令行提示符来输入命令。

如果bash是作为交互式shell启动的,它不会去访问/etc/profile文件,而会去用户的HOME目录检查.bashrc是否

存在。

在这个Linux系统上,这个文件看起来如下:

[jingpan@localhost ~]$ cat .bashrc 
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

# User specific aliases and functions

.bashrc文件有两个作用:一是查看/etc/目录下的共用bashrc文件,二是为用户提供了一个定制自己的命令别名(将在5.8节中讨论)和私有脚本函数(将在第16章中讲到)的地方。

通用的/etc/bashrc启动文件会被系统上每个启动交互式shell会话的用户执行。在这个Linux系统上它看上去是这样的:

[jingpan@localhost ~]$ cat /etc/bashrc
# /etc/bashrc

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

# It's NOT a good idea to change this file unless you know what you
# are doing. It's much better to create a custom.sh shell script in
# /etc/profile.d/ to make custom changes to your environment, as this
# will prevent the need for merging in future updates.

# are we an interactive shell?
if [ "$PS1" ]; then
  if [ -z "$PROMPT_COMMAND" ]; then
    case $TERM in
    xterm*)
        if [ -e /etc/sysconfig/bash-prompt-xterm ]; then
            PROMPT_COMMAND=/etc/sysconfig/bash-prompt-xterm
        else
            PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
        fi
        ;;
    screen)
        if [ -e /etc/sysconfig/bash-prompt-screen ]; then
            PROMPT_COMMAND=/etc/sysconfig/bash-prompt-screen
        else
            PROMPT_COMMAND='printf "\033]0;%s@%s:%s\033\\" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
        fi
        ;;
    *)
        [ -e /etc/sysconfig/bash-prompt-default ] && PROMPT_COMMAND=/etc/sysconfig/bash-prompt-default
        ;;
      esac
  fi
  # Turn on checkwinsize
  shopt -s checkwinsize
  [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
  # You might want to have e.g. tty in prompt (e.g. more virtual machines)
  # and console windows
  # If you want to do so, just add e.g.
  # if [ "$PS1" ]; then
  #   PS1="[\u@\h:\l \W]\\$ "
  # fi
  # to your custom modification shell script in /etc/profile.d/ directory
fi

if ! shopt -q login_shell ; then # We're not a login shell
    # Need to redefine pathmunge, it get's undefined at the end of /etc/profile
    pathmunge () {
        case ":${PATH}:" in
            *:"$1":*)
                ;;
            *)
                if [ "$2" = "after" ] ; then
                    PATH=$PATH:$1
                else
                    PATH=$1:$PATH
                fi
        esac
    }

    # By default, we want umask to get set. This sets it for non-login shell.
    # Current threshold for system reserved uid/gids is 200
    # You could check uidgid reservation validity in
    # /usr/share/doc/setup-*/uidgid file
    if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then
       umask 002
    else
       umask 022
    fi

    # Only display echos from profile.d scripts if we are no login shell
    # and interactive - otherwise just process them to set envvars
    for i in /etc/profile.d/*.sh; do
        if [ -r "$i" ]; then
            if [ "$PS1" ]; then
                . "$i"
            else
                . "$i" >/dev/null 2>&1
            fi
        fi
    done

    unset i
    unset pathmunge
fi
# vim:ts=4:sw=4
[jingpan@localhost ~]$ 

默认的文件会设置一些环境变量,但注意它并没有执行export命令让它们成全局的。记住,

交互式shell的启动文件只会在每次有新的交互式shell启动时才运行,因此任何子shell都会自动 执行

这个交互式shell的启动文件。

还能看出,/etc/bashrc文件也会执行位于/etc/profile.d目录下的那些应用专属的启动文件。

5.6.3 非交互式shell

 最后,一种类型是非交互式shell。系统执行shell脚本时用的就是这种shell。你不用担心它没有命令行提示符,

但当你每次在系统上运行脚本时仍要运行特定的启动命令。

为了处理这种情况,bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境

变量来查看要执行的启动文件。如果有指定的,shell会执行文件里的命令。

在这个Linux发行版里,默认情况下这个环境变量并未设置。

5.7 可变数组

环境变量很好的一个功能是它们可作为数组使用,数组是能够存储多个值的变量。值可按单

个值或整个数组来引用。

要给某个环境变量设置多个值,可以把值放在括号里,值与值之间用空格分隔:

[jingpan@localhost ~]$ mytest=(one two three four five)

没什么特别的地方。如果你想把数组当做普通的环境变量显示,你可能要失望了:

[jingpan@localhost ~]$ echo $mytest
one

只有数组的第一个值显示出来了。要引用一个单独的数组元素,你必须要用代表它在数组中

位置的数值索引值。数值要用方括号括起来:

[jingpan@localhost ~]$ echo ${mytest[2]}
three

要显示整个数组变量,可用星号作为通配符放在索引值的位置:

[jingpan@localhost ~]$ echo ${mytest[*]}
one two three four five

你也可以改变某个索引值位置的值:

[jingpan@localhost ~]$ mytest[2]=seven
[jingpan@localhost ~]$ echo ${mytest[*]}
one two seven four five
[jingpan@localhost ~]$ 

你甚至能用unset命令来删除数组中的某个值,但是要小心,这可能会有点复杂。看下面的例子:

[jingpan@localhost ~]$ unset mytest[2]
[jingpan@localhost ~]$ echo ${mytest[*]}
one two four five
[jingpan@localhost ~]$ echo ${mytest[2]}

[jingpan@localhost ~]$ echo ${mytest[3]}
four
[jingpan@localhost ~]$ 

这个例子用unset命令来删除在索引值为2位置的值。显示整个数组时,看起来像是索引里面

已经没这个索引了。但当专门显示索引值为2的位置的值时,能看到这个位置是空的。

最后,可以在unset命令后跟上数组名来删除整个数组:

[jingpan@localhost ~]$ unset mytest
[jingpan@localhost ~]$ echo ${mytest[*]}

5.8 使用命令别名

查看已有的别名列表,可用alias命令加-p参数

[jingpan@localhost ~]$ alias -p
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias vi='vim'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
[jingpan@localhost ~]$ 

你也可以用alias命令来创建自己的命令别名:

[jingpan@localhost ~]$ alias li='ls -il'
[jingpan@localhost ~]$ li
总用量 6644
24642419 -rwxr-xr-x   1 root    root     382752 1月  27 10:45 awk
24642401 dr-xr-xr-x   3 root    root       4096 11月  9 11:40 boot
24641707 drwxrwxr-x  10 jingpan jingpan    4096 11月 20 13:44 c
24642489 -rw-r--r--   1 root    root       2872 11月 28 11:15 centos74.xml
24642484 -rw-r--r--   1 root    root       2871 11月 28 09:26 centos7.xml
24642448 drwxrwxr-x   4 jingpan jingpan    4096 11月 22 14:18 c_example
24642474 drwxrwxr-x   2 jingpan jingpan    4096 11月 25 11:48 ch5
24646265 drwxrwxr-x   5 root    root       4096 2月   3 09:06 collector
24646263 -rw-rw-r--   1 jingpan jingpan   31105 2月   3 08:27 collector-linux.zip
24642471 drwxrwxr-x   4 jingpan jingpan    4096 11月 29 15:54 c_v
24646292 -rw-rw-r--   1 jingpan jingpan      92 2月   3 10:02 LICENCE.KEY
24642503 drwxrwxr-x   6 jingpan jingpan    4096 3月   3 10:29 linux
24642430 drwxrwxr-x   3 jingpan jingpan    4096 11月 22 15:56 linux_c
24642525 drwxrwxr-x  19    1274    1274    4096 2月   3 08:45 net-snmp-5.7.2
24642524 -rw-r--r--   1 root    root    6281352 2月  20 2014 net-snmp.tar.gz
24641823 drwxr-xr-x   9 jingpan jingpan    4096 11月  8 08:26 nginx-1.12.2
24642480 -rw-r--r--   1 root    root        718 11月 27 16:42 rpm_check.txt
24642523 -rwxr--r--   1 root    root       6608 10月 23 2015 snmp_install.sh
24642492 drwxr-xr-x   4 root    root       4096 1月  27 11:48 test
24641557 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 公共的
24641556 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 模板
24641561 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 视频
24641560 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 图片
24641558 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 文档
24641555 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 下载
24641559 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 音乐
24641554 drwxr-xr-x.  2 jingpan jingpan    4096 11月  6 12:58 桌面

一旦定义了命令别名,你就能在任何时候在shell中使用了,包括shell脚本中。

命令别名的行为和局部环境变量差不多,它们通常只在定义它们的shell进程中有效:

[jingpan@localhost ~]$ alias li='ls -il'
[jingpan@localhost ~]$ bash
[jingpan@localhost ~]$ li
bash: li: command not found

当然,现在你知道了解决这个的办法。bash   shell在启动交互式shell时总会读取位于$HOME/.bashrc的启动文件。

那里是创建命令别名的好地方(如先前指出的.bashrc文件的注释里提到的)。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值