什么是环境变量?
如下,在shell中定义一个变量,这个变量会被这个shell的子shell或者当前这个shell的进程所继承。
首先,在当前的shell中定义了一个HOLIDAY变量,定义变量用key=value
的形式,等号两边不可有空格,key
常用大写,value
可以选用引号括起来,但如果value
中有空格或者特殊字符时一定要用引号。
[dpc@aliyun ~]$ HOLIDAY=happy
[dpc@aliyun ~]$ echo $HOLIDAY
happy
进入子shell,查看HOLIDAY
的值,发现子shell中没有这个变量
[dpc@aliyun ~]$ sh
sh-4.2$ echo $HOLIDAY
退出子shell,回到原来的shell查看变量是存在的。
sh-4.2$ exit
exit
[dpc@aliyun ~]$ echo $HOLIDAY
happy
用export
将变量声明到所有的shell中,重新进入子shell,查看是否有变量HOLIDAY
。
[dpc@aliyun ~]$ export HOLIDAY
[dpc@aliyun ~]$ sh
sh-4.2$ echo $HOLIDAY
happy
现在我们可以看到HOLIDAY
变量的值可以被打印。因为现在HOLIDAY
已经成为环境变量(Environment variables),这个环境变量在当前shell中定义,并被所有子shell和进程继承。在被export
之前,HOLIDAY
变量只是一个shell变量,不是环境变量,其只在当前的shell下有效。其实shell变量就是一个局部变量,环境变量就是一个全局变量,直接声明的是一个局部变量,通过export
命令声明的是一个全局变量。
如果要查看环境变量,可以使用printenv
、env
、set
这三个命令,前两个命令是等效的。
[dpc@aliyun ~]$ printenv
XDG_SESSION_ID=19590
HOSTNAME=aliyun
SHELL=/bin/bash
TERM=xterm
HISTSIZE=1000
SSH_CLIENT=113.87.184.183 3761 22
HOLIDAY=happy
SSH_TTY=/dev/pts/0
USER=dpc
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:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=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:*.webm=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:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
MAIL=/var/spool/mail/root
PWD=/home/dpc
LANG=en_US.UTF-8
HISTCONTROL=ignoredups
HOME=/home/dpc
SHLVL=2
LOGNAME=dpc
SSH_CONNECTION=113.87.186.183 3761 172.18.4.152 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/printenv
OLDPWD=/root
printenv
、env
、set
的区别:
-
printenv
可用于查看单个环境变量,此时和用echo
查看环境变量是等效的[dpc@aliyun ~]$ printenv SHELL /bin/bash [dpc@aliyun ~]$ echo $SHELL /bin/bash
-
env
可用于修改变量,printenv
与env
查看的环境变量是一样的 -
set
可查看所有的shell变量[dpc@aliyun ~]$ printenv | wc -l 23 [dpc@aliyun ~]$ env | wc -l 23 [dpc@aliyun ~]$ set | wc -l 57
一些常见的环境变量:
SHELL
:用于解释当前输入的命令的shell,通常默认是bash,另一种常见的是sh
TERM
:当前运行的终端类型,常见是xterm
USER
:当前登录的用户
PWD
:当前的工作目录
OLDPWD
:前一次的工作目录,此变量用于使用cd -
命令切换到前一次的工作目录
LS_COLORS
:定义了ls
命令输出的不同类型文件显示的颜色
MAIL
:当前用户邮箱的路径
PATH
:系统查找命令时的目录列表。当用户输入一个命令时,系统按顺序从该列表中的目录查找可执行的命令
LANG
:当前的语言和本地化设置,通常是系统语言和字符编码
HOME
:当前用户的家目录,root用户为/root
,其它用户为/home/username
_
:前一次执行的命令
除了环境变量,还有一些常见的shell变量。注意shell变量用echo $variable_name
查看
-
BASHOPTS
:当前bash被执行时使用的选项列表 -
BASH_VERSION
:当前bash的版本 -
BASH_VERSINFO
:当前bash的版本 -
COLUMNS
:当前窗口用于打印输出的列数,改变终端窗口大小列数随之变化 -
DIRSTACK
:当前pushd
和popd
命令可用的目录堆栈 -
HISTFILESIZE
:可存储到文件的中最大历史命令行数 -
HISTSIZE
:可存储到内存中的最大历史命令行数 -
HOSTNAME
:本机的hostname -
IFS
:内部字段分隔符(Internal Field Separator),用于分隔命令行中的输入,默认是空格 -
PS1
:主要的命令提示符,定义了启动一个shell会话时的每行开头的提示符。PS2
用于定义当一个命令跨多行时进行辅助声明的提示符。 -
SHELLOPTS
:可以用set
命令设置的shell选项 -
UID
:当前用户的UID
设置和取消变量
将环境变量降为shell变量
[dpc@aliyun ~]$ export -n HOLIDAY
此时HOLIDAY
将不再为环境变量
[dpc@aliyun ~]$ printenv HOLIDAY
但是它仍为shell变量
[dpc@aliyun ~]$ set | grep HOLIDAY
HOLIDAY=happy
如果想完全取消变量,把环境变量和shell变量都取消掉,可以使用unset
命令
[dpc@aliyun ~]$ unset HOLIDAY
可验证此时HOLIDAY
变量已经不存在了
[dpc@aliyun ~]$ echo $HOLIDAY
登录shell与非登录shell
当我们启动一个shell时它会去读取配置文件,但是在不同的启动方式下,它读取的配置文文件也不同。
shell会话有四个概念:
- 交互式(Interactive)
- 非交互式(Non-Interactive)
- 登录(Login)
- 非登录(Non-Login)
交互式和非交互式好理解,我们在有GUI(图形界面)的linux的终端上输入命令或者通过xshell连接服务器后输入命令都是在交互式shell会话中执行。而我们在终端中运行某个shell脚本,此时这个shell脚本是在非交互式shell会话中执行的。由此也可知,一个终端会话可以是这两种的结合。
对于交互式shell和非交互式shell,它们的类型不影响我们执行命令时使用的环境变量。
对于登录(Login)和非登录(Non-Login),两者比较让人迷惑。
比如我们通过ssh登录一个需要验证的远程服务器时,我们此时的shell会话就是一个登录shell会话。
[dpc@aliyun ~]$ ssh root@120.77.1.1
root@120.77.1.1‘s password:
在我们输入密码后,登录到了shell,此时由登录shell进入了非登录shell。
登录shell就是我们还没有输入密码登录到这个shell,当进入非登录shell时,意味着我们已经验证过了当前用户的密码登录到了该用户的shell中。
最简单的判断登录还是非登录shell的方法是:在当前的shell中,输入echo $0
, 查看结果。非登录shell的输出是bash
或su
,登录shell的输出是-bash
或-su
。
[root@aliyun ~]# echo $0
-bash
[root@aliyun ~]# su dpc
[dpc@aliyun root]$ echo $0
bash
[dpc@aliyun root]$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
上面我们首先以root
用户登录了shell,此时为登录shell。然后切换到了dpc
用户,此时为非登录shell,我们查看非登录shell时的环境变量,使用的仍然是root
用户的环境变量。
再试下下面的一种情况
[root@aliyun ~]# su - dpc
Last login: Thu Sep 24 00:23:07 CST 2020 on pts/0
[dpc@aliyun ~]$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dpc/.local/bin:/home/dpc/bin
我们会发现用su dpc
切换用户时没有验证密码,进入了登录shell,使用的是原来的root用户的环境变量;用su - dpc
切换用户时验证了密码,进入了非登录shell,使用的是dpc用户的环境变量。
以下三个命令是等效的,都可以进入非登录shell。
su - dpc
su -l dpc
su --login dpc
登录shell和非登录shell的环境变量有何不同呢?
登录shell执行脚本的顺序
- 登录shell执行
/etc/profile
-
/etc/profile
执行/etc/profile.d
目录下的脚本 - 然后执行当前用户的
~/.bash_profile
-
~/.bash_profile
执行当前用户的~/.bashrc
-
~/.bashrc
执行/etc/bashrc
非登录shell执行脚本的顺序
- 非登录shell首先执行
~/.bashrc
- 然后
~/.bashrc
执行/etc/bashrc
-
/etc/bashrc
调用/etc/profile.d
目录下的脚本
参考
[1] environment and shell
[3] Linxu环境变量
[4] System Variables
[5] Difference Between Login Shell and Non-Login Shell
[6] Login Shell Non-Login Shell
[7] Interactive and non-interactive shell
[8] How To Read and Set Environmental and Shell Variables on a Linux VPS