文章目录
- 问题
- 知识点
- 问题分析及解决
- 补充
问题
在使用linux
桌面环境(通常是ubuntu/debian/deepin
等版本的linux
)的时候,新增环境变量后,会使用source /etc/profile
命令使新的环境变量立刻生效而不用重新启动系统。但经常会遇到使用source /etc/profile
命令之后,新的环境变量只能在当前终端里面有效,而在新打开的终端中,使用新的环境变量就找不到了。
先通过操作来复现一下这个过程,我使用的linux
分支是deepin
(其他版本的类似),通过图形界面打开终端操作进入,准备添加一个新的变量testEnv
。
# 查看系统中,是否已经存在名字为 testEnv 的环境变量,如果输出为空,表示环境变量不存在
echo $testEnv
# 环境变量不存在,就添加 testEnv 到环境变量
sudo bash -c "echo testEnv=888888 >> /etc/profile"
# 输出/etc/profile最后一行,查看是否添加成功
tail -1 /etc/profile
# testEnv=888888
# 使 testEnv 环境变量生效
source /etc/profile
# 查看环境变量 testEnv 的值,如果输出 888888 ,表示环境变量创建成功
echo $testEnv
# 888888
新建一个终端窗口,看下是否可以输出环境变量testEnv
的值。
# 输出结果为空
echo $testEnv
但是,只要重启系统,一切就正常了。
问题在于为什么?
知识点
我们知道,linux
中环境变量是通过配置文件添加的,在系统启动的时候,通过加载不同的配置文件来初始化环境变量,所以环境变量找不到的问题,归根结底是配置文件的加载问题。搞清楚linux
中配置文件的加载顺序,上面遇到的问题也就不难理解了。
shell
是用户与Linux
系统进行交互的媒介,它在运行时具有两种属性,即“交互”与“登陆”,由此衍生出交互式登录和非交互式登录两种模式。
- 交互式登录
shell
进程是指直接通过某终端输入账号密码后登录打开的shell
进程,如使用ssh
或者堡垒机进行远程主机连接,或者使用su - username
或者su -l username
执行的登录用户切换等。 - 非交互式登录
shell
进程:图形界面下打开的终端、su username
执行的登录切换、直接运行脚本或者bash -c
执行一段命令的时候。
linux
中常见的配置文件有/etc/profile
、~/.bash_profile
、~/.bashrc
、/etc/bashrc
和/etc/profile.d/*
。
-
/etc/profile
:此文件为系统的每个用户设置环境信息,系统中每个用户登录时都要执行这个脚本,如果系统管理员希望某个设置对所有用户都生效,可以写在这个脚本里,该文件也会从/etc/profile.d
目录中的配置文件中搜集shell的设置。 -
~/.bash_profile
:每个用户都可使用该文件设置专用于自己的shell信息,当用户登录时,该文件仅执行一次。默认情况下,他设置一些环境变量,执行用户的.bashrc文件。 -
~/.bashrc
:该文件包含专用于自己的shell信息,当登录时以及每次打开新shell时,该文件被读取。 -
/etc/bashrc
:为每一个运行bash shell的用户执行此文件,当bash shell被打开时,该文件被读取。
关于配置文件加载顺序的说法,网上有很多,看起来也不尽相同,其实主要区别在与不同的linux
发行版,包含的配置文件也是有很大区别的,即使有名字相同的配置文件,配置文件里面的内容也是大相径庭。但是基本上所有的linux
发行版都会包括/etc/profile
(登录后读取的第一个配置文件)和~/.bashrc
两个配置文件。
对于交互式登录shell来说,一定会读取的配置文件的顺序是/etc/profile --> ~/bashrc
。
对于非交互式登录shell来说,一定会读取的配置文件属性是~/.bashrc
,如果~/.bash_profile
存在的话,顺序是这样的~/.bash_profile --> ~/.bashrc
。
对于其他的各种常见配置文件的读取,以及是读取,主要取决于/etc/profile
或者 ~/bashrc
里面的具体内容,比如在centos7
的/etc/profile
中有如下一段代码,是用来载入/etc/profile.d/*.sh
配置文件的。
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
deepin
系统中的.bashrc
里面有一段代码,用来读取命令别名声明文件。
# Get the aliases and functions
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
所以,系统配置文件的读取顺序,需要根据系统版本的不同去了解读取顺序。
每次在配置文件中配置的新环境变量,只会对随后新启动的shell
进程生效,如果想让配置文件立即生效,需要让shell进程重读配置文件,这就需要进行类似source /etc/profile
或者 . /etc/profile
这样的操作。
问题分析及解决
一般遇到本文开头说到的问题的同学,都是在图形界面下使用终端的配置的环境变量,然后通过source /etc/profile
重新读取配置文件,然后打开一个新的终端的时候,使用命令就是出现comand not found
的问题。
结合上面的知识点看下,在图形界面下使用的终端属于非交互式登录的shell,以我使用的deepin
系统为例,新开一个终端时,读取的配置文件应该是~/.bashrc
。而新增的环境变量配置在/etc/profile
,所以出现在新开终端找不到环境变量的问题。
解决办法:
- 在新开终端,执行
source /etc/profile
,比较麻烦 - 将环境变量配置在,
~/.bashrc
中,可以解决 - 在
~/.bashrc
中添加source /etc/profile
,这样就可以为每一个新开的终端重新读取一次/etc/profile
配置文集,可以解决,多次读取/etc/profile
没有发现有什么副作用。
补充
网上早期很多关于java
、node
等的环境变量配置的文章,感觉都是针对搭建centos
服务器或者测试机的,他们之所以没有这个问题,是因为服务器基本都是通过ssh
远程登录,是交互式登录模式的,每次打开一个新的远程连接,都会重新读取/etc/profile
,所以不会有command not found
的问题。而个人用的电脑,基本都是图形界面直接操作,所以会遇到比较多的这种问题。
<完>