Linux環境變量
- Linux環境變量
- github地址
- 前言
- 1. 基本概念
- 環境變量的本質
- 2. 認識常見的環境變量
- PATH
- 查看PATH
- 修改PATH
- HOME
- SHELL
- 其他常見環境變量
- PWD與OLDPWD
- LOGNAME與USER
- SSH_TTY
- 由環境變量理解權限
- 使用系統調用獲取環境變量
- 理解權限
- 3. 總結什么是環境變量
- 3. 命令行參數和環境變量的全局性
- 命令行參數
- main函數的第三個參數
- 環境變量具有全局屬性
- 4. 環境變量可以被子進程繼承
- 5. 本地變量與內建命令
- 本地變量
- 本地變量不能被子進程繼承
- 內建命令
- 結合系統調用體會內建命令cd
- 6. 結語
Linux環境變量
github地址
有夢想的電信狗
前言
? 環境變量是操作系統為我們維護的一組 key=value
格式的鍵值對,它們像“隱形助手”一樣,悄悄地決定了程序的運行行為、指令的查找路徑以及用戶的工作環境。
? 你是否好奇為什么在終端中輸入 ls
就能列出文件,而運行自己寫的程序卻要加上 ./
?你是否遇到過不同用戶登錄后,默認目錄和權限有所不同?這一切的背后,其實都離不開環境變量的支持。
? 本文將帶你深入理解環境變量的本質、常見環境變量的作用、如何查看與修改環境變量,以及它們與命令行參數、本地變量、內建命令之間的聯系。通過豐富的動圖演示和詳盡的代碼解析,讓你從操作層面到系統原理都能全面掌握 Linux 環境變量的精髓。
1. 基本概念
? 在 Linux 操作系統原理中,環境變量(Environment Variables) 是一類在 操作系統層面定義的全局變量,用于配置操作系統行為和影響進程運行環境。它們通常以 鍵=值
的形式存在,作用范圍可以是用戶會話、進程,也可以被繼承到子進程。
- 環境變量(environment variables)一般是指在操作系統中用來指定操作系統運行環境的一些參數
- 如:我們在編寫
C/C++
代碼的時候,在鏈接的時候,從來不知道我們的所鏈接的動態靜態庫在哪里,但是照樣可以鏈接成功,生成可執行程序,原因就是有相關環境變量幫助編譯器進行查找。 - 環境變量通常具有某些特殊用途,還有在系統當中通常具有全局特性
環境變量的本質
- 本質上是 以字符串形式存儲在內存中的一組鍵值對。
- 每個運行中的進程都維護著一份自己的環境變量表,初始化來源是其父進程
2. 認識常見的環境變量
PATH
- PATH:Linux系統的指令搜索路徑
- 為什么我們輸入系統的指令(
ls pwd
)運行時不需要加./
,而運行我們自己寫的程序運行時需要加./
- 為什么我們輸入系統的指令(
系統當中,針對于指令的搜索,Linux系統會為我們提供一個環境變量PATH。
- 環境變量
PATH
是系統開機就已經在shell
中存在的
查看PATH
查看PATH
的內容:
我們查看環境變量,需要借助echo
指令。
echo PATH
:直接輸出PATH
,PATH
默認會被當成字符串處理- 查看
PATH
中的內容需要使用$
。echo $PATH
查看任何環境變量的值都需要在環境變量名前加上$,一般使用命令echo $envName
# 查看環境變量的內容
echo $PATH # $表示取環境變量的內容
修改PATH
PATH的內容是一系列路徑,路徑與路徑之前用:
分隔
- 輸入
ls
,shell
會在這些被冒號分隔開的路徑中依次查找ls程序。- 找到的話,執行。因為
ls
處于路徑/usr/bin
路徑下,路徑/usr/bin
位于環境變量PATH
中,所以系統可以找到直接執行
- 找到的話,執行。因為
- 輸入我們自己的程序
mycmd
,該程序不在PATH
中包含的各個路徑下,mycmd
程序所處的路徑也不在環境變量PATH
中,因此找不到,最終提示command not found
。這個查找工作一般都是shell
來完成的
那么這就意味著,我們自己編寫的程序,想要像指令一樣不加路徑直接運行,有兩種方法:
- 將我們的自己的程序放入PATH中存在的路徑中:
- 以程序
mycmd
為例,只需要將其移動到環境變量PATH
中的目錄即可 - 使用
mv mycmd /usr/bin
命令,常見系統的指令通常位于/usr/bin
目錄下
- 以程序
- 將我們自己的程序所處的路徑加入到
PATH
環境變量中
接下來我們演示第二種方法,并演示PATH
的修改
- 環境變量
PATH
的訪問和修改方法
# 查看環境變量的內容
echo $PATH # $表示取環境變量的內容
# 修改
PATH=$PATH:newContent
# PATH=newPath # 錯誤行為 會把PATH中之前的內容覆蓋掉
? 修改環境變量使用等號=
,這里的等號可以類比為賦值,賦值會把PATH中之前的內容覆蓋掉。我們需要將指定的路徑追加到PATH
中,因此指令為:PATH=$PATH:newPath
,這里的行為依然是覆蓋,但是$PATH
保證了我們取出了PATH
中已有的內容,并在后面加上:newPATH
,保證了修改是追加新路徑
- 通過這種方式修改的
PATH
,是一種內存級別的PATH,在shell中保存。 - 如果我們錯誤的修改了
PATH
,只需關閉XShell
后重新登陸shell
。 shell
沒有啟動時,PATH
在系統的配置文件中保存。shell
啟動時,環境變量從系統的配置文件中加載到內存which
指令搜索時,也是在環境變量PATH
中搜索的
HOME
- HOME : 指定用戶的主工作目錄(即用戶登陸到Linux系統中時,默認的目錄)
- 為什么我們在首次登錄
Linux
時,默認所處的目錄是自己的家目錄呢? - 為什么默認所處的路徑為
/home/userName
呢
- 為什么我們在首次登錄
? 原因是,當我們登錄時,shell
會識別當前登錄的用戶名,給當前用戶填充$HOME
環境變量,填充的值為/home/用戶名
當我們使用Xshell
遠程登錄Linux
時,xshell會為我們分配命令行解釋器,命令行解釋器會執行類似cd $HOME
的命令,因此初次登錄時,處于當前用戶的家目錄
SHELL
- SHELL:當前終端使用的Shell程序,它的值通常是/bin/bash
- 如何查看當前系統中使用的是哪一個
shell
呢? - 只需要查看環境變量
SHELL
中的值,命令為:echo $SHELL
- 如何查看當前系統中使用的是哪一個
# 查看當前系統中正在使用的shell
echo $SHELL
- 經驗證,默認使用的
shell
一般是/bin/bash
其他常見環境變量
? 除了以上環境變量,系統中還有很多其他的環境變量。
環境變量如此多,我們進行查看呢?我們可以使用env
命令
- env:查看到當前的進程和bash進程從系統中繼承下來的所有環境變量。
PWD與OLDPWD
圖中的標記對一些常見的環境變量進行了解釋,還有一些不同的Linux發行版會顯示的其他環境變量。
-
環境變量PWD:記錄當前進程所處的工作路徑
-
環境變量OLDPWD:會記錄當前進程所處的工作路徑的上個路徑
- 我們的
cd -
命令會被解析為cd $OLDPWD
,因此可以跳轉到上次所處的路徑中
- 我們的
LOGNAME與USER
- 可以看到,當我們在不同用戶之間切換時,env獲取到的環境變量值中的
LOGNAME與USER
也都在同步變化。
SSH_TTY
這里對環境便令SSH_TTY
簡單提一下。SSH_TTY
表示當前終端所使用的字符設備文件。當前SSH_TTY
的值為/dev/pts/0
- 我們再開啟一個終端,向該文件中追加內容時,另一個終端中便會顯示我們追加的內容,如下所示
由環境變量理解權限
使用系統調用獲取環境變量
? 我們可以通過系統調用接口getenv()
來獲取環境變量,需要給該函數傳入字符串格式的環境變量名。該函數返回環境變量值,格式為char*風格的字符串,如下所示
理解權限
-
我們Linux的權限理解部分,同一個文件,不同的用戶在使用時,擁有不同的操作權限
- 那么系統想要給我們限制權限,首先要獲取到我們是誰。
- 操作系統可以通過系統調用來獲取當前用戶是誰
-
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>// 環境變量,可以讓程序獲取到當前系統中的用戶是誰int main() {char who[32];strcpy(who, getenv("USER"));if (strcmp(who, "root") == 0) {printf("你是root用戶, 可以做任何事情\n");} else {printf("你是普通用戶, 受到權限約束\n");}return 0;}
-
不同的用戶執行相同的代碼時,環境變量獲取到的值就不一樣,因此可以操作系統可以辨識出當前使用用戶的身份
- 有了環境變量,程序內部便可以獲取環境變量,從而獲知當前是哪個用戶正在執行指令的
有了環境變量的存在,系統就有了獲知當前是哪個用戶正在執行指令的能力,獲取后就可以將文件屬性中的擁有者、所屬組和文件所對應的權限進行比對,進而判定當前用戶有無特定的權限
3. 總結什么是環境變量
經過以上對環境變量的熟悉和使用,我們可以得出環境變量的最終概念
環境變量是操作系統提供的一組name=value
形式的變量,不同的環境變量有不同的用途,通常具有全局屬性
環境變量的本質
- 本質上是 以字符串形式存儲在內存中的一組鍵值對。
- 每個運行中的進程都維護著一份自己的環境變量表,初始化來源是其父進程。
功能 | 示例變量 | 說明 |
---|---|---|
系統路徑 | PATH | 指定可執行程序查找路徑 |
當前用戶信息 | USER , HOME | 當前登錄用戶和主目錄 |
默認語言 | LANG | 控制系統語言和字符集 |
編輯器 | EDITOR | 系統默認文本編輯器 |
庫路徑 | LD_LIBRARY_PATH | 指定動態庫搜索路徑 |
shell 類型 | SHELL | 當前 shell 程序路徑 |
3. 命令行參數和環境變量的全局性
命令行參數
什么是命令行參數?
- 命令行參數:是指在命令行界面運行程序時,跟隨在程序名稱后的額外輸出參數,用于控制程序的運行或傳遞數據
我們C/C++
的main
函數是可以有參數的
int main(int argc, char* argv[]) {return 0;
}
- 其中:
argc
是一個整數,argv
是一個字符串數組(指針數組)argv
字符串數組的大小由argc
決定
main
函數也是函數,因此main函數可以調用其他函數,同時也可以被其他函數調用。C/C++
程序運行起來時,int argc
和char* argv[]
這兩個參數會被調用方進行傳參。
-
用戶層面上:
main
函數是被第一個調用的函數 -
系統層面上:第一個調用的函數是Startup()函數或CRTStartup()函數,該函數會調用
main
函數,給main
函數傳參
使用以下代碼查看char* argv[]
中的內容:
int main(int argc, char* argv[]) {int i = 0;for (; i < argc; ++i) {printf("argv[%d]->%s\n", i, argv[i]);}return 0;
}
可以看到char* argv
中的內容,就是我們輸入的指令和附帶的選項。
argv[0]
是./mycmd
argv[i]
是后面附帶的不同選項
為什么會這樣呢?
這是因為,我們在**bash
中輸入的指令,本質上都是字符串,bash會把他們以空格為分隔符,分割成子字符串,分割成多少個字符串,就初始化argc
為多少**
打散后 , argv
指針數組中,每個空間,存放的都是相應字符串的地址。最后一個空間存放的是空指針NULL
-
在系統層面上,被打散的字符串的地址存入
char* argv[]
中并初始化argc
。shell
命令行解釋器,會把這兩個參數傳遞給程序的main
函數。 -
既然
char* argv[]
的最后一個位置的值為NULL
,那么我們遍歷argv[]
就有了新的方式:-
for循環的第二個位置表示循環終止條件,當
argv[i]
的值為NULL
時,循環結束,也就完成了對argv[]
的遍歷 -
int main(int argc, char* argv[]) {int i = 0;for (; argv[i]; ++i) {printf("argv[%d]->%s\n", i, argv[i]);}return 0;}
-
-
以上就是命令行參數。以空格為分隔符。將輸入的指令分割的過程,就叫做命令行解析
為什么要有main函數要有命令行參數?
- 我們
Linux
中的指令都是C語言寫的main
函數的命令行參數為指令、工具、軟件提供命令行選項的支持- 調用同一個程序時,使用不同的選項,可以完成不同的功能
main函數的第三個參數
main函數不只有argc和argv兩個參數,還可以帶第三個參數,如下:
int main(int argc, char* argv[], char* env[]) {return 0;
}
-
這里的第三個參數
char* env[]
表示的是從父進程繼承過來的環境變量,其內容和env
命令輸出的內容一模一樣-
使用以下代碼驗證
-
// C/C++ 程序 兩張表 main函數的第三個參數// 1. 命令行參數 向量表 2. 環境變量 向量表 這兩張表的結構一模一樣int main(int argc, char* argv[], char* env[]) {int i = 0;// argv 和 env 的最后一個元素存儲的是NULL,遍歷到結尾時,條件自動為假,所以退出循環for (; env[i]; ++i) {printf("env[%d]->%s\n", i, env[i]);}return 0;}
-
- 綜上:通過參數和
char* env[]
查看的環境變量和env
命令查看到的環境變量一模一樣char* argv[]
和char* env[]
的結構一模一樣,且最后一個元素都是NULL
指針
- 不能簡單的認為程序啟動時,就是簡單的將程序加載到內存。
而是程序在啟動時,Startup
函數會調用main
函數,給main
函數傳遞參數,傳遞兩張表。命令行參數表和環境變量表。
環境變量具有全局屬性
? 我們在命令行下所運行的所有進程,都是bash
的子進程。bash
在啟動的時候,會從操作系統的配置文件中讀取環境變量信息。子進程會繼承父進程交給我的環境變量。子進程可以通過main
函數的第三個參數char* env[]
訪問環境變量。
- 而子進程再創建子進程,環境變量表就被無窮盡的傳遞下去了,因此說,環境變量具有全局性
環境變量也是進程的數據,進程具有獨立性,父子進程fork
之后的代碼共享,數據,讀時共享,寫時拷貝
- 一個進程還沒有創建子進程時,如果已經創建好了環境變量,再創建子進程,就會被子進程繼承下去
- 環境變量的繼承有兩種方式
main
函數傳參- 直接繼承
4. 環境變量可以被子進程繼承
如何驗證環境變量可以被子進程繼承呢?
我們可以在系統的環境變量中加入一個我們自己的環境變量
增加環境變量命令:
export [name=value]
# env可以查看全部的環境變量
# echo $PATH 等可以查看特定環境變量的內容
# 那么如何增加環境變量# 增加該環境變量 MY_VALUE=123456
export MY_VALUE=123456
export
增加環境變量后,env
命令中就多了我們添加的環境變量
export
增加環境變量后,我們運行的bash
的子進程./mycmd
中,獲取到的環境變量也有我們新增加的環境變量
這是因為:環境變量可以被子進程繼承
- 發源進程只有一個,是
bash
,因此**bash
及其的所有子進程構成的進程樹,擁有相同的環境變量**
如果今天我想讓我的所有進程都遵守一套規則,我可以把這套規則放在bash的環境變量中,這樣所有的子進程都會有相同的環境變量
系統中的權限,每條指令都應該遵守,這些功能的實現就和環境變量有關
取消環境變量:
# 取消我們剛剛新增的環境變量 MY_VALUE
unset MY_VALUE
bash
進程取消環境變量MY_VALUE
后,之后的子進程就也沒有相應的環境變量了,讀者可以自行驗證
- 另外,編譯器在編譯時,會對main函數的命令行參數進行檢查,會進行條件編譯。如果
main
函數有參數,會傳入相應個數的參數。沒有參數,就不傳入參數
5. 本地變量與內建命令
本地變量
與環境變量對應的還有本地變量。
什么是本地變量?本地變量就是我們直接在shell
中定義的變量
# 直接在bash命令行中定義
a=1
b=2
c=3
本地變量只會在本bash
內部有效,不會被子進程繼承
set命令:可以顯示系統中所有的變量。包括環境變量和本地變量
本地變量不能被子進程繼承
- 我們用本地變量
MYVAL
來測試,以下程序查看MYVAL
的值
int main(int argc, char* argv[], char* env[]) {printf("MYVAL: %s\n", getenv("MYVAL")); return 0;
}
內建命令
- 觀察以下輸出,思考問題
- 前文提到,
bash
命令行中執行的程序,都是bash
的子進程。 - 前文提到,本地變量只在本
bash
內部有效,不會被子進程繼承 - 那么,
echo $MY_val
命令,echo
也是Bash
的子進程,./mycmd
也是bash
的子進程,無法獲取到本地變量。echo
也是bash
的子進程,為什么能取到本地變量的?
我們糾正之前的結論:命令行中所啟動的指令,不一定全都要創建子進程
我們的命令要被分成兩類:
- 常規命令:執行時,
bash
通過創建子進程執行 - 內建命令:執行時,
bash
不創建子進程,而是bash
自己親自執行的。類似于bash調用了自己寫的或者系統提供的函數。已知常見的內建命令如下:echo
:echo是由bash執行的,bash內部有一個函數叫echo
,echo $MY_val
時,echo獲取到bash內部的本地變量輸出出來就行了cd
:每個進程都有自己的當前工作目錄,如果cd命令在執行時創建子進程,那么cd改變的將是子進程的工作目錄,而不會改變主進程的工作目錄
結合系統調用體會內建命令cd
? bash也是一個程序,其內部在實現時,讀取到用戶輸入cd時,bash并不會創建子進程,而是直接調用系統調用chdir
改變bash
程序當前的路徑。
我們可以用如下代碼進行驗證:
由于我們執行mycmd時,bash會創建子進程,因此我們無法直接觀察到bash的工作路徑改變,我們可以觀察/proc
目錄下進程的cwd
目錄
// 驗證 cd 命令
int main(int argc, char* argv[]) {printf("before change:\n");sleep(40);if (argc == 2) {chdir(argv[1]);}printf("change end:\n");sleep(20);return 0;
}
- sleep()是為了給我們輸入指令查看進程的cwd留出時間
通過C語言提供的外部變量獲取環境變量
// 通過C語言提供的 外部變量 environ 獲取環境變量
int main() {int i = 0;extern char** environ;for (; environ[i]; ++i) {printf("%s\n", environ[i]);}return 0;
}
6. 結語
? 至此,我們已經系統地學習了 Linux
環境變量的方方面面:它們的結構、用途、修改方式、繼承機制,以及它們在程序啟動和系統權限管理中的關鍵角色。從 PATH
到 HOME
,從命令行參數到環境變量表,從本地變量的局部性到子進程的繼承性,環境變量貫穿了整個 Linux 系統的運行脈絡。
? 希望這篇文章不僅幫助你掌握了環境變量的使用技巧,更讓你對 Linux 操作系統背后的邏輯有了更深的理解。
以上就是本文的所有內容了,如果覺得文章對你有幫助,歡迎 點贊?收藏 支持!如有疑問或建議,請在評論區留言交流,我們一起進步
分享到此結束啦
一鍵三連,好運連連!
你的每一次互動,都是對作者最大的鼓勵!
征程尚未結束,讓我們在廣闊的世界里繼續前行!
🚀