
文章目錄一.環境變量1.什么是環境變量?2.windows下配置PATH環境變量的原理3.一覽常見的環境變量4.指令方式如何查看環境變量5.PATH-存放系統默認搜索路徑的環境變量6.兩種方法使運行我們寫的可執行程序不帶路徑二.環境變量和本地變量三.和環境變量相關的命令1.echo:顯示某一特定環境變量2.export: 設置新的環境變量3.env: 顯示所有環境變量4.set:查看本地定義的本地變量四.命令行參數1.cmd下的命令行參數2.main函數的三個參數3.指令的選項制作原理五.代碼獲取環境變量的三種方式1.char* getenv(const char* str)2.char * env[]3.extern char** environ六.環境變量具體應用1.身份驗證2.su 和su -的區別一.環境變量1.什么是環境變量?
環境變量是有特殊用途的系統變量。
如:我們在編寫C/c++代碼的時候,在鏈接的時候,從來不知道我們的所鏈接的動態靜態庫在哪里,但是照樣可以鏈接成功,生成可執行程序,原因就是有相關環境變量幫助編譯器進行查找。
2.windows下配置PATH環境變量的原理
這里推薦一個b站迷糊老師的一個視頻哦:迷糊老師:windows環境變量
學習Java的朋友都經歷過配置環境變量:

大家在安裝JDK的時候,驗證是否安裝成功,都會在命令行里輸入java和javac命令來驗證,那你明白這背后的原理嗎?
我們知道java和javac其實都是可執行程序.exe:
javac.exe作用: 編譯代碼
java.exe作用:執行代碼
它們在硬盤上都有自己的路徑,如下圖:

那么如果我們在編寫好Hello.java代碼文件,準備使用javac.exe去編譯代碼的時候,去使用javac.exe的前提是找到javac.exe,但是如果沒有把javac.exe的路徑添加到PATH環境變量,也就是沒有配置好環境變量的話,系統就找不到javac.exe在哪里,自然也就無法編譯Hello.java代碼文件。
相反的,如果我配置好了環境變量,你想編譯代碼的時候,系統就能按照PATH環境變量所提供的路徑去找Javac.exe工具,找到后就可以使用起來,編譯我們寫好的代碼!
windows下環境變量和我們今天講的linux下環境變量的原理和作用都是類似的,只不過是使用和細節上有差異! 例如:一個環境變量存儲的字符串可能由多條路徑組成,在windows下和Linux下它們的路徑分隔符就有所差異 windows下:

Linux:

3.一覽常見的環境變量代碼語言:JavaScript代碼運行次數:0運行復制
當前登入用戶:USER=li當前登入用戶的家目錄:HOME=/home/li當前工作目錄:PWD=/home/li系統默認搜索路徑:PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/li/.local/bin:/home/li/bin
4.指令方式如何查看環境變量


5.PATH-存放系統默認搜索路徑的環境變量
,指令也是程序,那為什么我們執行我們自己寫的可執行程序要帶./filename路徑,而執行系統安裝的可執行程序卻不要帶上任何路徑!
其實要執行指令,前提都是要找到該指令的路徑,只不過系統安裝的可執行程序所在的位置已經被添加到了PATH的環境變量中,而PATH環境變量存放了系統的搜索路徑,簡而言之就是系統的可執行程序系統能自動搜索到,所以執行時可以不帶路徑;
但是我們自己寫的可執行程序不在系統的搜索路徑下,也就是系統自動查找你的可執行程序查找不到,所以你在執行代碼的時候得自己帶上./相對路徑,這用絕對路徑來執行也是可以的!
6.兩種方法使運行我們寫的可執行程序不帶路徑
要想使得我們
前面我們說到,系統指令所在的目錄被添加到了PATH環境變量中, 在執行可執行程序的時候,系統就能通過依次查找PATH中的記錄路徑來查找到系統指令,所以我只要把我寫的可執行程序加入到系統安裝的系統指令的目錄,這樣系統默認的搜索路徑中就可找到我寫的可執行程序了!
ps:
系統安裝的系統指令目錄是:/usr/bin/提權su- 切換到root或者sudo 提權執行,拷貝(安裝)操作代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos test]$ ./hellohello world[li@VM-8-5-centos test]$ hello-bash: hello: command not found[li@VM-8-5-centos test]$ sudo cp hello /usr/bin[li@VM-8-5-centos test]$ hellohello world
但是這種方法我們極不推薦,因為我們寫的可執行程序沒有經過測試,就放到系統安裝指令的路徑,可能會造成指令的污染!
話接上文,我們還可以直接把我們自己寫的可執行程序所在路徑添加到系統默認搜索路徑PATH中.
代碼語言:javascript代碼運行次數:0運行復制
//刪除文件用rm -rf,刪除一個環境變量/本地變量用unset[li@VM-8-5-centos test]$ sudo rm -rf /usr/bin/hello[li@VM-8-5-centos test]$ ./hellohello world[li@VM-8-5-centos test]$ hello-bash: /usr/bin/hello: No such file or directory[li@VM-8-5-centos test]$ pwd/home/li/1-16/test//export導入環境變量//PATH環境變量名,$PATH環境變量的內容//export PATH=/home/li/1-16/test會覆蓋掉原來的$PATH,所以用冒號分隔不同路徑,意為相加[li@VM-8-5-centos test]$ export PATH=$PATH:/home/li/1-16/test[li@VM-8-5-centos test]$ hellohello world[li@VM-8-5-centos test]$
二.環境變量和本地變量
env命令只能夠顯示所有的環境變量,但是set命令能顯示所有的環境變量+所有的本地變量
代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos ~]$ myval=100[li@VM-8-5-centos ~]$ env | grep myval[li@VM-8-5-centos ~]$ set | grep myvalmyval=100[li@VM-8-5-centos ~]$ export youval=200[li@VM-8-5-centos ~]$ env | grep youvalyouval=200[li@VM-8-5-centos ~]$ set | grep youvalyouval=200
環境變量類似全局變量,具有全局屬性,本地變量類似局部變量,類似局部變量.
全局還是局部屬性體現在當創建子進程的時候是否繼承父進程能夠被繼承!
但是注意:環境變量具有全局屬性也只是針對本次登入,要永久有效需要修改.bashrc文件!(這涉及到剛登入bash的時候為什么就有環境變量的原因,只需了解,不用深究)
ps:
.bashrc在每一個用戶的家目錄中都能通過ls -al找到代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos ~]$ cd ~[li@VM-8-5-centos ~]$ pwd/home/li[li@VM-8-5-centos ~]$ ls -al | grep .bashrc-rw-r--r-- 1 li li 350 Dec 28 20:51 .bashrc
.bashrc文件內容是腳本語言編寫,如果/etc/bashrc存在,就導入/etc/bashrc的內容,每一次登入bash,因為每一個用戶的家目錄都有這么一個.bashrc文件,所以對應用戶的家目錄中的.bashrc文件的內容就會被導入到env環境變量中(此條均為自己猜測,個人理解)。

環境變量是既然是內存級別的東西,那么下次登入就類似內存掉電失去,需要修改,bashrc相關文件才能永久性更改環境變量。

殘留問題:我們知道echo是bash的子進程,本地變量不會被父進程在創建子進程的時候被繼承,那為什么可以通過echo $本地變量名的方式打印到命令行終端?—–內建命令! 解答: Linux下大部分命令都是通過子進程的方式執行的! 但是,還有一部分命令,不通過子進程的方式執行,而是由bash自己執行,這種命令叫做內建命令!
三.和環境變量相關的命令echo: 顯示某個環境變量值export: 設置一個新的環境變量env: 顯示所有環境變量set: 顯示本地定義的shell變量(本地變量)和環境變量unset: 清除環境a變量1.echo:顯示某一特定環境變量代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos ~]$ echo $PWD/home/li
2.export: 設置新的環境變量代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos ~]$ export youval=200

3.env: 顯示所有環境變量

4.set:查看本地定義的本地變量

5.unset:清除環境變量
刪除文件用rm -rf, 刪除一個環境變量/本地變量用unset
代碼語言:javascript代碼運行次數:0運行復制
unset 環境變量名
代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos test]$ myval=400[li@VM-8-5-centos test]$ echo $myval400[li@VM-8-5-centos test]$ unset myval[li@VM-8-5-centos test]$ set | grep myval
代碼語言:javascript代碼運行次數:0運行復制
[li@VM-8-5-centos test]$ export youval=800[li@VM-8-5-centos test]$ echo $youval800[li@VM-8-5-centos test]$ unset youval[li@VM-8-5-centos test]$ echo $youval[li@VM-8-5-centos test]$ env | grep youval[li@VM-8-5-centos test]$ set | grep youval
四.命令行參數1.cmd下的命令行參數
在windows下相信你肯定寫過所謂的關機小程序,那你肯定用到過這個:

或許你會說這不就是一個選項嘛,啊,他確實是選項,可是這選項的背后原理依賴的就是命令行參數!
2.main函數的三個參數
熟悉c語言的童鞋們都知道main函數也是有三個參數,函數main()可以有或沒有參數列表,通常最多支持3個參數:
int main()int main(int argc, char *argv[])int main( int argc, char *argv[] , char *env[])
main()函數一般用int或者void形的。用int型定義main更好些,因為在結束的時候可以返回給操作系統一個值以表示執行情況。
這里先講一講前兩個參數的情況:
argc全稱:Argument count 參數個數argv全稱:Argument vector 參數向量代碼語言:javascript代碼運行次數:0運行復制
1 #include<stdio.h> 2 int main(int argc,char* argv[]) 3 { 4 for(int i=0;i<argc;i++) 5 { 6 printf("argv[%d]->%sn",i,argv[i]); 7 } 8 return 0; 9 }
當我們在bash輸入命令的時候,輸入的命令就以空格為間隔,把命令劃分為一個個小的子字符串,然后他們的首字符的地址被存入到argv中,argv是一個指針數組,數組里面存放著argc個元素,每一個元素的類型是char*類型.
3.指令的選項制作原理代碼語言:javascript代碼運行次數:0運行復制
#include<stdio.h>#include<string.h>int main(int argc,char* argv[]){ for(int i=0;i<argc;i++) { if(argc!=2) { printf("Usage:nt%s [-a/-b/-c/-ab/-ac/-bc/-abc]n",argv[0]); return 1; } if(strcmp("-a",argv[1])==0) { printf("功能an"); return 1; } if(strcmp("-b",argv[1])==0) { printf("功能bn"); return 1; } if(strcmp("-c",argv[1])==0) { printf("功能cn"); return 1; } if(strcmp("-ab",argv[1])==0) { printf("功能abn"); return 1; } if(strcmp("-ac",argv[1])==0) { printf("功能acn"); return 1; } if(strcmp("-bc",argv[1])==0) { printf("功能bcn"); return 1; } if(strcmp("-abc",argv[1])==0) { printf("功能abcn"); return 1; } } return 0;}
選項的原理我想通過 看懂上面這段代碼你就能夠明白了!

五.代碼獲取環境變量的三種方式1.char* getenv(const char* str)
a.getenv參數的介紹:

代碼語言:javascript代碼運行次數:0運行復制
/return搜索返回值

b.getenv獲取指定環境變量
代碼語言:javascript代碼運行次數:0運行復制
#include<stdio.h>#include<string.h>#include<stdlib.h>int main(){ printf("USER:%sn",getenv("USER")); return 0;}
2.char * env[]代碼語言:javascript代碼運行次數:0運行復制
#include<stdio.h>#include<string.h>int main(int argc,char* argv[],char* env[]){ //env沒有個數的限制NULL->0 for(int i=0;env[i];i++) { printf("env[%d]->%sn",i,env[i]); }return 0;}
3.extern char** environ#include
#include<stdio.h>#include<string.h>#include<unistd.h>int main(){ //使用第三方變量envieron //extern告訴編譯器,我要用庫里的environ這個變量了,鏈接的時候你給我鏈接進來 extern char** environ; for(int i=0;environ[i];i++) { printf("environ[%d]->%sn",i,environ[i]); } return 0;}
其實方法2的char * env[] 和方法3中的char** environ本質上是一樣的:
char* env[]作為形參,其實是降維成一個二級指針的,可寫作char** env,另外參數名也是自定義的,本質是char env 和char* environ就是一個形參一個實參的關系

六.環境變量具體應用1.身份驗證
我們怎么用環境變量來實現某個用戶是否有某個文件的各種權限吶?除了我們之前講的文件權限的內容之外,我們還有一個前提就是系統怎么知道當前登入用戶是誰?
其實這環境變量信息在我們登入shell的時候就被導入進來了,只是一個strcmp的過程!簡單,安排!
代碼語言:javascript代碼運行次數:0運行復制
#include<stdio.h>#include<string.h>#include<stdlib.h>int main(){ char* who=getenv("USER"); //strcmp if(strcmp(who,"root")==0) { printf("USER:rootn"); printf("USER:rootn"); printf("USER:rootn"); printf("USER:rootn"); printf("USER:rootn"); } else { printf("權限不足n"); } return 0;}

2.su 和su -的區別
su和su -都可以從普通用戶切換到root,區別在于su切換的前后環境變量的USER都是普通用戶
而su -切換的前后環境變量的USER發生從普通用戶到root的改變.
su展示:

su -展示:

從兩張圖我們可以看出,su-是重登了用戶,完全改變了身份,可能和.bashrc的重新加載有關,所以路徑也在su-的時候發生改變.