6.使用Linux环境变量
学习内容在《Linux命令行与shell脚本编程大全(第3版)》一书当中,欢迎大家一起来学习!
文章目录
前言
环境变量有啥作用?
Linux环境变量可以帮助我们提升Linux shell的体验。
因为很多程序和脚本都通过环境变量来获取系统信息、存储临时数据和配置信息。
在Linux系统当中有很多地方都可以设置环境变量,了解去哪里设置相应的环境变量十分重要
学完本章可以收获哪些内容?
(1)Linux环境变量的存储位置
(2)环境变量如何使用
(3)怎样创建自己的环境变量
6.1 什么是环境变量
bash shell用一个叫环境变量的特性来存储有关shell会话和工作环境的信息。这个特性允许我们在内存当中存储数据,以便程序或shell中运行的脚本能够轻松访问到他们。
在bash shell中,环境变量一般分为两类:
(1)全局变量
(2)局部变量
6.1.1 全局环境变量
全局环境变量对于shell会话和所有生成的子shell都是可见的。
局部环境变量只对创建他们的shell可见。
因此,只有全局环境变量才能使子shell获取父shell的信息
查看全局环境变量的命令:env 或 printenv
不带参数会列出所有的全局变量
[root@machine001 ~]# printenv HOME
/root
也可使用echo命令:
[root@machine001 ~]# echo $HOME
/root
6.1.2 局部变量变量
局部环境变量只能在定义他们的进程中可见。
Linux系统默认定义了标准的局部环境变量。
自己也可以定义,自己定义的局部变量被称为用户定义局部变量。
Linux系统并没有一个只显示局部环境 变量的命令。set命令会显示为某个特定进程设置的所有环境变量,包括局部变量、全局变量 以及用户定义变量。
[root@machine001 ~]# set
...
6.2 设置用户定义变量
6.2.1 设置局部用户定义变量
一旦启动了bash shell,或者执行一个shell脚本,就可以创建在这个shell进程内可见的局部变量,通过’='赋值,等号两端不能有空格,值可以是数值或字符串。
[root@machine001 ~]# echo $my_name
[root@machine001 ~]# my_name='mj'
[root@machine001 ~]# echo $my_name
mj
注:所有的环境变量名均使用大写字母,这是bash shell的标准惯例。如果自己要创建局部变量或者shell脚本,尽量使用小写字母。在涉及到用户定义的局部变量时,坚持使用小写字母,这能够避免重新定义系统环境变量可能带来的灾难。
6.2.2 设置全局环境变量
创建全局环境变量的方法是先创建一个局部环境变量,然后再把它导出到全局变量当中。
[root@machine001 ~]# my_name='global mj'
[root@machine001 ~]# export my_name
[root@machine001 ~]# echo $my_name
global mj
[root@machine001 ~]# bash
[root@machine001 ~]# echo $my_name
global mj
从上述命令我们可以看出,我们在父shell当中创建的局部变量导出到全局变量后,在子shell当中也可以取到该变量的值。
注:
(1)修改子shell中的全局变量不会影响父shell中该变量的值
(2)子shell无法使用export命令改变父shell中全局变量的值
6.3 删除环境变量
可以使用unset命令来删除环境变量:
[root@machine001 ~]# echo $my_name
global mj
[root@machine001 ~]# unset my_name
[root@machine001 ~]# echo $my_name
[root@machine001 ~]#
工作小技巧:在涉及到环境变量名时,如何判断何时在变量名前加$?
(1)如果要使用变量,则加$
(2)如果操作变量(修改,删除),则不加$
(3)printenv 变量 这个命令操作例外
在删除全局变量时,需要注意:
如果在子进程当中删除了一个全局环境变量,那么这只对子进程有效,该全局变量在父进程当中仍然可用。
6.4 默认的shell环境变量
默认情况下,bash shell会用一些特定的环境变量来定义系统环境。这些变量在你的Linux系 统上都已经设置好了,可以直接使用。
默认的shell环境变量较多,这里就不一一列举,用到时自查就好。
6.5 设置 PATH 环境变量
当在shell命令行界面输入一个外部命令时,shell必须搜索系统来找到对应的程序。PATH环境变量定义了用于进行命令和程序寻找的目录。
[root@machine001 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/module/jdk1.8.0_301/bin:/opt/module/hadoop/bin:/opt/module/hadoop/sbin:/opt/module/hive/bin:/opt/module/spark-yarn/bin:/root/bin
输出当中有10个可以供shell用来查找的命令和程序。PATH中的目录使用冒号分隔。
如果命令或者程序的位置没有包括在PATH变量中,那么如果不使用绝对路径的话,shell是没办法找到的。
[root@machine001 ~]# myprog
-bash: myprog: command not found
因此,为了解决该问题,我们可以向PATH环境变量中添加包含存放应用程序的目录
[root@machine001 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/module/jdk1.8.0_301/bin:/opt/module/hadoop/bin:/opt/module/hadoop/sbin:/opt/module/hive/bin:/opt/module/spark-yarn/bin:/root/bin
[root@machine001 ~]# PATH=$PATH:/home/neu/bin
[root@machine001 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/opt/module/jdk1.8.0_301/bin:/opt/module/hadoop/bin:/opt/module/hadoop/sbin:/opt/module/hive/bin:/opt/module/spark-yarn/bin:/root/bin:/home/neu/bin
[root@machine001 ~]# test.sh
majie
在将目录添加到PATH环境变量之后,就可以在虚拟目录结构中的任何地方执行程序。
注:如果希望子shell也能找到自己写的程序的位置,需要把修改后的PATH环境变量导出。
#JAVA_HOME
export JAVA_HOME=/opt/module/jdk1.8.0_301
export PATH=$PATH:$JAVA_HOME/bin
#HADOOP_HOME
export HADOOP_HOME=/opt/module/hadoop
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin
#HIVE_HOME
export HIVE_HOME=/opt/module/hive
export PATH=$PATH:$HIVE_HOME/bin
#SPARK_HOME
export SPARK_HOME=/opt/module/spark-yarn
export PATH=$PATH:$SPARK_HOME/bin
6.6 定位系统环境变量
在登入Linux系统启动一个bash shell时,默认情况下bash会在几个文件中查找命令。这些文件叫作启动文件或环境文件。bash检查的启动文件取决于你启动的bash shell的方式。启动bash shell有3种方式:
(1)登陆时作为默认登录shell
(2)作为非登录shell的交互式shell
(3)作为运行脚本的非交互shell
6.6.1 登录shell
当你登录Linux系统时,bash shell会作为登录shell启动。登录shell会从5个不同的启动文件里读取命令:
(1)/etc/profile
(2)$HOME/.bash_profile
(3)$HOME/.bashrc
(4)$HOME/.bash_login
(5)$HOME/.profile
/etc/profile文件是系统上默认的bash shell的主启动文件。系统上的每个用户登录时都会执行 这个启动文件。
6.6.1.1 /etc/profile文件
/etc/profile文件是bash shell默认的主启动文件,只要登录Linux系统,bash就会执行/etc/profile启动文件中的命令。下面注解里也提到,一般是不改动该文件内容的,想要添加环境或相关脚本,可以在/etc/profile.d/目录当中添加。
[root@machine001 etc]# cat 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 /usr/sbin
pathmunge /usr/local/sbin
else
pathmunge /usr/local/sbin after
pathmunge /usr/sbin after
fi
HOSTNAME=`/usr/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 /etc/profile.d/sh.local ; do
if [ -r "$i" ]; then
if [ "${-#*i}" != "$-" ]; then
. "$i"
else
. "$i" >/dev/null
fi
fi
done
unset i
unset -f pathmunge
上述脚本当中,使用到了for循环来遍历执行**/etc/profile.d/*目录下的.sh和sh.local这些脚本:
[root@machine001 profile.d]# ll
total 68
-rw-r--r-- 1 root root 771 Sep 27 2018 256term.csh
-rw-r--r-- 1 root root 841 Sep 27 2018 256term.sh
-rw-r--r--. 1 root root 196 Mar 25 2017 colorgrep.csh
-rw-r--r--. 1 root root 201 Mar 25 2017 colorgrep.sh
-rw-r--r--. 1 root root 1741 Apr 11 2018 colorls.csh
-rw-r--r--. 1 root root 1606 Apr 11 2018 colorls.sh
-rw-r--r--. 1 root root 80 Apr 11 2018 csh.local
-rw-r--r-- 1 root root 1706 Sep 27 2018 lang.csh
-rw-r--r-- 1 root root 2703 Sep 27 2018 lang.sh
-rw-r--r--. 1 root root 123 Jul 31 2015 less.csh
-rw-r--r--. 1 root root 121 Jul 31 2015 less.sh
-rw-r--r-- 1 root root 377 Jan 26 11:57 my_env.sh
-rw-r--r--. 1 root root 81 Apr 11 2018 sh.local
-rw-r--r-- 1 root root 105 Apr 11 2018 vim.csh
-rw-r--r-- 1 root root 269 Apr 11 2018 vim.sh
-rw-r--r--. 1 root root 164 Jan 28 2014 which2.csh
-rw-r--r--. 1 root root 169 Jan 28 2014 which2.sh
6.6.1.2 $HOME目录下的启动文件
其他的启动文件都起着共同的作用:提供一个用户专属的启动文件来定义该用户所用到的环境变量。
大多数Linux发行版只用到这四个启动文件中的一个到两个:
(1)$HOME/.bash_profile
(2)$HOME/.bashrc
(3)$HOME/.bash_login
(4)$HOME/.profile
[root@machine001 ~]# ls -a | grep -e "bash" -e "profile"
.bash_history
.bash_logout
.bash_profile
.bashrc
注:以.号开头的文件说明是隐藏文件
shell会按照下列顺序,运行第一个被找到的文件,余下的则会被忽略:
$HOME/.bash_profile
$HOME/.bash_login
$HOME/.profile
注:这个列表中并没有$HOME/.bashrc文件。这是因为该文件通常通过其他文件运行的。
[root@machine001 ~]# 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
[root@machine001 ~]#
.bash_profile启动文件会先去检查HOME目录中是不是还有一个叫.bashrc的启动文件。如果有 的话,会先执行启动文件里面的命令。
注:$HOME 和 ~ 的作用相同。
6.6.2 交互式shell进程
如果你的bash shell不是登录系统时启动的(比如是在命令行提示符下敲入bash时启动),那 么你启动的shell叫作交互式shell。
如果bash是作为交互式shell启动的,那么它不会访问/etc/profile文件,只会检查用户HOME目录中的.bashrc文件
[root@machine001 ~]# cat .bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
bashrc文件有两个作用:一是查看/etc目录下通用的bashrc文件,二是为用户提供一个定制自己的命令别名和私有脚本函数的地方。
6.6.3 非交互式shell
系统执行shell脚本时用的就是这种shell。不同的地方在于它没有命令行提示符。但是当你在系统上运行脚本时,也许希望能够运行一些特定启动的命令。
为了处理这种情况,bash shell提供了BASH_ENV环境变量。当shell启动一个非交互式shell进程时,它会检查这个环境变量来看要执行的启动文件。如果有指定的文件,shell会执行该文件里的命令,这通常包括shell脚本变量设置。
在CentOS Linux发行版当中,该变量默认情况下并没有设置,那shell脚本应该到哪里去获取它们的环境变量呢?
有些shell脚本是通过启动一个子shell来执行的,子shell可以继承父shell导出的变量。
例如:父shell是登录shell,在/etc/profile、/etc/profile.d/*.sh和$HOME/.bashrc文件中 设置并导出了变量,用于执行脚本的子shell就能够继承这些变量。
注:由父shell设置但并未导出的变量都是局部变量。子shell无法继承局部变量。
对于那些不启动子shell的脚本,**变量已经存在于当前shell中了。**所以就算没有设置 BASH_ENV,也可以使用当前shell的局部变量和全局变量。
6.6.4 环境变量持久化
Q:如何创建通用的永久性全局变量或局部变量?
A:最好是在**/etc/profile.d目录中创建一个以.sh结尾的文件**。把所有新的或修改过的全局环境变量设置放在这个文件中。
Q:存储个人用户永久性bash shell变量的地方?
A:$HOME/.bashrc文件
6.7 数组变量
环境变量有一个很酷的特性就是,它们可作为数组使用。数组是能够存储多个值的变量。这些值可以单独引用,也可以作为整个数组来引用。 要给某个环境变量设置多个值,可以把值放在括号里,值与值之间用空格分隔。
[root@machine001 ~]# test=(one two three)
[root@machine001 ~]# echo $test
one
[root@machine001 ~]# echo $test[1]
one[1]
[root@machine001 ~]# echo ${test[1]}
two
[root@machine001 ~]#
注:索引值需要用方括号括起来,然后需要用花括号进行解析
要显示整个数组变量,可用星号作为通配符放在索引值的位置。
[root@machine001 ~]# echo ${test[*]}
one two three
也可以使用uset删除数组中某个值
[root@machine001 ~]# unset test[0]
[root@machine001 ~]# echo ${test[*]}
two three
数组变量这个特性在shell脚本编程时不常用。