在 shell 中,有两种设置环境变量的方式:直接设置和使用 export 命令。
直接设置环境变量
直接设置环境变量的方式是将变量名和值用等号连接起来,例如:
|
|
这种方式设置的环境变量只在当前 shell 会话中有效,当会话结束时,变量也会被删除。
验证:
输入
|
|
输出:
|
|
再次输入
|
|
输出:
|
|
可以看到,直接设置的环境变量只在当前 shell 会话中有效。
使用 export 设置环境变量
使用 export 命令设置环境变量的方式是将变量名和值用等号连接起来,并在变量名前加上 export,例如:
|
|
这种方式设置的环境变量不仅只在当前 shell 会话中有效,还会被传递给子进程 。也就是说,当我在当前 shell 会话中设置了一个环境变量,然后在子进程中执行某个命令,这个命令就可以使用这个环境变量。
验证:
输入
|
|
输出:
|
|
再次输入
|
|
输出:
|
|
可以看到,使用 export 命令设置的环境变量不仅只在当前 shell 会话中有效,还会被传递给子进程。
延伸
之所以会来思考两种不同的环境变量设置方式这个问题,是因为在跑我之前的 ToDolist 后端时发现,当时为了 Docker,在 application.properties 里把数据库的连接写成了 spring.datasource.url=jdbc:mysql://${MYSQL_URL:mysqldb}/${MYSQL_DB:todolistdb},我现在想在本地跑的话,就要传入 MYSQL_URL 的值。
一开始天真地想用 export 环境变量传入,结果当然还是 idea 连不上数据库。后来咨询专业人士,结合相关文档的阅读,收获了以下的一些小知识。
-
对于我这个项目,
MYSQL_URLMYSQL_USERNAMEMYSQL_PASSWORD这些可以直接在代码里写死(后面在不同服务器上跑,要反复修改代码,非常不方便,直接 pass),也可以用 args(每次启动都要输入一遍多个参数,也很麻烦),一般都是放在环境变量里。 -
需要在 idea 里的
Run/Debug Configurations里设置Environment variables。我自己在一个 shell 进程里设置了环境变量,idea 不是 shell 开的,它不知道 shell 有什么环境变量,也不知道我设置的是在哪个进程,对于它来说读取比较麻烦,所以需要专门在 idea 里面进行设置。 -
那么问题又来了,每个 shell 进程里的环境变量是不一样的吗?关了一个进程后,再开新的进程里为什么会有我之前设置的环境变量?
答案是
execve。execve函数的定义如下:1 2int execve(const char *pathname, char *const _Nullable argv[], char *const _Nullable envp[]);一个 shell 进程会用
execve来启动用户请求的命令。当我在命令行输入export ENV=1命令并按下回车时, shell 会解析命令并将其分解为命令本身和参数,调用fork系统调用创建一个新的子进程,在子进程中, shell 会使用execve来加载并执行输入的命令,像我的环境变量就通过envp传递,当命令执行完毕后,子进程会退出,并返回执行结果。也就是说,当我启动新的进程的时候,shell 帮我复制到
execve的envp参数上了,新开的进程就有了这些环境变量。