環境變量PATH
什麼是環境變量PATH?這個環境變量,在調用shell腳本的時候很有用,麻煩和問題也主要集中在這裡,PATH環境變量定義了各種shell環境搜尋執行指令的路徑,就像windows上的path環境變量一樣。執行自己寫的腳本,如果你不想進入到腳本目錄或者輸入全路徑,那麼你直接把你的腳本拷貝到PATH下面的任意一個目錄即可,但是這樣太侵入了,不優雅,下面簡單介紹一種比較優雅的開發技巧。
開發技巧:便捷執行指令
通過軟連接配接和别名alias來讓自定義腳本可以在目前shell環境中任意被調用。
第一步:檢視PATH環境變量,随便選一個目前使用者有操作權限的一個出來,本處選擇/usr/local/sbin[host:HanQingwu]
第二步:寫一個測試腳本run.sh,放在/var/www/html/HalleySite 目錄下,裡面隻有一句指令”echo $0"列印目前執行腳本名稱,然後做一個軟連接配接ln -s /var/www/html/HalleySite/start_nginx_and_uwsgi.sh /usr/local/sbin/start
第三步:定義别名,先alias -p看一下,不要定義重複了,然後定義别名alias start="sh start",完成
結果:你不用在進入到腳本所在的目錄執行腳本,在任意目錄執行run指令就可以調用.sh腳本,而且支援tab鍵補全,是不是很NB!
一、概述
環境變量:bash shell用一個稱作“環境變量(environment variables)”的特性來存儲有關shell會話和工作環境的資訊,它允許你在記憶體中存儲資料,以便運作在shell上的程式和腳本通路,這些資料可以用來識别使用者、賬戶、系統、shell特性以及任何其他你需要存儲的資料。
shell中的環境變量有全局環境變量和局部環境變量,通過KV(variable=value)的形式聲明一個局部變量,export這個局部變量,則更新成為全局環境變量。既然shell分開來用,顯然兩者有差別,下面分開來讨論。
1. 全局環境變量
特性:聲明一個全局環境變量,在目前shell程序以及子shell程序中可用,父shell程序中不可用,這裡的“可用”可以了解成父shell程序全局環境變量的一個copy,而不是繼承父類的全局環境變量兩者共用一份,是以子shell程序中對父shell程序的全局環境變量進行增、删、該、查均無影響。
證明一:1)全局變量在目前shell可用、子shell可用,但是父shell程序不可用;2)子shell copy了父shell程序的全局環境變量的副本,是以子shell對變量的操作對父類透明
?
2. 局部環境變量
特性:目前shell程序可用,父、子shell程序不可用
證明:父程序定義局部變量,子程序通路不到;子程序定義局部變量,父程序通路不到
?
3. 總結
1. 全局環境變量:目前shell程序及子程序可用,父shell程序不可用,子程序對變量的修改對目前shell來說透明無影響;
2. 局部環境變量:目前shell程序可用,父程序、子程序均不可用;
3. 環境變量存在記憶體當中,斷電後消失,如果想要在下一次系統加電後還可以通路:
a. 如果這個變量是系統級别的(所有使用者共享),則可以在/etc/profile中export;
b. 如果這個變量隻對目前使用者有效,則可以在~/.bash_profile(或者~./bash_login,或者~./profile,或者~./bashrc,或者/etc/bashrc)中export;
補充:本節讨論主要是fork程序方式,不包括source和exec,三種方式的差異在後面進行讨論;
二、啟動shell
環境變量存放在記憶體當中,斷電後消失。但是你會發現類似USER、PATH、LOGNAME、HOSTNAME這些環境變量,隻要系統一啟動就存在了。這是為什麼?
作業系統因該具備至少5個功能:記憶體管理、檔案系統管理、程序管理、裝置管理(硬體)和使用者接口管理,這裡的使用者接口不能了解成init 5啟動界面功能或類似windows的界面,而是使用者和系統核心互動的接口,即核心提供給外界的一組通路接口,是以在系統啟動的時候,啟動shell就是為此而生,他向使用者提供了一組和Linux kernel互動的接口。
啟動shell随着系統的啟動而啟動,根據表現可以将其了解成後續登入shell、互動shell、非互動shell的頂級父shell環境,是以在啟動shell中聲明的全局環境變量,後續的所有shell都可以通路到,毫無疑問,局部變量例外。
啟動shell啟動之後,他首先會到/etc/profile中讀取指令并執行,讀取執行該檔案,主要做了兩件事:
1. 定義并 聲明全局的環境變量,export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL,所有的shell程序都可以通路到這些變量;
2. 到/etc/profile.d目錄中執行該目錄下的應用啟動檔案,這個目錄集中存放了一些應用的啟動檔案,這些應用随着系統啟動而啟動,譬如vim、less等,類似于windows中的啟動項中定義的一些開機自運作的軟體;
/etc/profile.d目錄下的内容和/etc/profile的内容如下:

?
三、登入shell
無論是哪一個使用者登入系統,printenv列印的全局環境變量都是一樣的,因為這些值在啟動shell中定義完成。但是你會發現,實際上存在不一樣,這會給人造成錯覺,進而無法了解登入shell的真正含義。
1. 差別啟動shell和登入shell
系統啟動完成之後,緊接着就是使用者登入通路了。不同的使用者登入系統,printenv的結果是不一樣的,也就是說不同的使用者有不同的的shell程序環境,這也間接說明登入shell和啟動shell是不一樣,登入shell是頂級環境。先來驗證一下,再來進行說明。
驗證一:不同使用者登入,其全局環境變量不一樣,此處選擇root和work使用者,測試全局環境變量PATH
步驟一:work賬戶登入,echo $PATH > /home/work/var.path
步驟二:切換使用者以及環境到root,su - root,echo $PATH >> /home/work/var.path
步驟三:cat /home/work/var.path,比較兩行值,執行結果如下所示
?
2. 登入shell詳解
登入shell在啟動shell之上又加入了個性化,不同使用者獲得的登入環境是不一樣的。除root使用者之外,Linux上其他使用者的主目錄是/home/xxx,要進行個性化,必然在此做文章。
登入shell的入口,~/.bash_profile檔案。
a. .bash_profile檔案
當你輸入使用者名和密碼登入系統的時候,啟動shell程序會fork一個登入shell程序,這個登入shell程序會到目前登入使用者的主目錄下順序讀取(如果存在)檔案~/.bash_profile、~/.bash_login、~/.profile 中的指令并執行,需要注意的是這三個檔案不一定都存在(我的系統centos 2.6.32中就隻存在.bash_profile),如果存在多個,則按照列出順序進行讀取指令并執行。
讀取到.bash_profile主要做了兩件事情,一件是source(最後一節會有source的詳解)~/.bashrc這個檔案,另一件是重新export PATH,詳細内容如下。
?
b. .bashrc檔案
由此,可知不同使用者的環境變量PATH不一緻的原因。但是source ~/.bashrc這個檔案又做了什麼事情?
.bashrc檔案也做了兩件事,一件是定義一些别名(定義别名,你可以在目前shell中任意使用,類似定義局部環境變量),另一件事是source了/etc/bashrc檔案。
?
c. /etc/bashrc檔案
/etc/bashrc又TM的做了什麼事情?在/etc/bashrc檔案中也做了兩件事情,一件事情是定義了一些局部變量(沒有export,登入shell可用,其fork的子shell均不能通路),還根據UID設定了umask的權限;另一件事情是又讀取并執行了一次/etc/profile.d目錄下的檔案,這一步和啟動shell有重複,雖然看似重複但實際上是有必要的原因,這裡了需要和互動shell(下一節講解)結合。
至此,使用者登入環境初始化完成,所有使用者公共的全局變量有了,不同使用者個性化的全局變量也準備好了,和自己有關系的局部變量也準備好了,萬事俱備,使用者可以進行系統操作了。
?
四、互動shell
init 3登入系統之後,你在指令行界面輸入的指令實際上都是在登入shell的環境中執行,你可以通路登入shell中聲明的任意局部變量。但是當你輸入bash顯式啟動一個互動shell的時候(指令行界面給你的感覺是敲了一下回車,剩下的好像什麼也沒發生),你會發現粗大事了,還是在一模一樣的界面,但是你發現通路不了剛才可以通路到的局部變量了。這是因為登入shell fork了一個子shell的環境,而子shell環境是不能通路父shell環境的局部變量的。
1. 差別登入shell和互動shell
證明一:互動shell不是登入shell,而是登入shell fork的一個子shell程序
步驟一:登入系統後,定義一個局部變量testing="zhangsan",指令行輸入echo $testing,測試可以通路得到;
步驟二:指令行輸入bash啟動一個互動shell,通路剛才定義的局部變量,echo $testing,測試不可以通路;
步驟三:指令行輸入exit指令,退出互動式shell,再次通路echo $testing,發現可以通路;
?
2. 互動shell詳解
當你在指令行輸入bash指令顯式進入互動式shell環境的時候,系統發生了什麼?不像啟動shell和登入shell,互動式shell沒有發生在系統啟動的時候,也沒有發生在使用者登入的時候,這時候系統已經啟動,使用者已經登入,之前的讀取的檔案執行的指令和結果都已經在記憶體中存在了,那麼互動式shell做了那些事情呢?
互動shell是登入shell的子程序,是以他擁有了登入shell聲明的所有全局環境變量(當然也擁有啟動shell聲明的全局環境變量),但是他沒有登入shell聲明的局部變量,也沒有登入shell聲明的一些個性化例如umask、alias等,那麼目前使用者沒有任何理由開啟一個互動式shell,個性化丢失,每次開啟都要重新搞一遍個性化,太TM麻煩了,這種功能你用嗎?有毛用啊?
請不要激動,存在即合理。
互動式shell啟動第一步,就是檢查目前使用者的主目錄下是否有.bashrc檔案(這個檔案上一節有詳細的描述),存在則讀取并執行檔案中的指令。這個檔案的作用不就是聲明局部變量和個性化配置的的嗎?他第一步就是定義别名等,然後source /etc/bashrc檔案,/etc/bashrc檔案一開始定義了使用者的局部變量,接着又加載并執行了/etc/profile.d目錄下的應用啟動檔案。
你會驚奇的發現,開啟一個互動shell,和一個剛剛登入的登入shell環境一模一樣!幹淨、純粹、原始,就像少女一樣啊!激動吧?!還有更激動的事情,不要忘了,子shell可以通路父shell的全局環境變量,但是父shell不能通路子shell的環境,也就意味着你在互動式shell中所做的壞事永遠不會影響到父shell環境(當然你不要手賤去改配置檔案,改了配置檔案就是持久化了操作内容,而不是更改記憶體内容,系統斷電後再啟動加載檔案還是會還原出來的),當然如果想父shell環境也能感受到,則需要修改檔案了。
實際上你也能感受到,登入shell其實就是第一個互動shell,一問一答,使用者問系統答,很友好啊,有求必應!
五、非互動shell
非互動shell就沒有那麼友好了,這也正是非互動shell存在的核心價值,不需要使用者來幹預,自動完成腳本中規定的指令。
是以,簡單來說,非互動shell,即是執行shell腳本。
當然,非互動式shell作為互動shell fork出來的子程序,擁有父程序所有的全局環境變量(再次申明:不擁有父程序的局部環境變量,你在腳本中是通路不到的),他不會再像互動shell那樣到使用者主目錄下去讀取執行.bashrc檔案給自己做個性化,最簡單直接的例子就是ls的alias指令ll,你在腳本中使用該指令,就會收到”run.sh: line 3: ll: command not found“的錯誤提示,如以下代碼示例。
?
要想對非互動是shell進行個性化,系統也提供了”接口“,就是環境變量BASH_ENV,當啟動一個非互動式shell的時候,系統會檢查這個環境變量來檢視要加載執行的啟動檔案,是以你可以自定義一個檔案路徑然後export BASH_ENV,進而達到你想要的目的和結果,這也是傳說中的BASH_ENV漏洞,但是這個漏洞在2014.09月被修複了,在此就不再進行贅述了。
1)BASH_ENV漏洞簡單利用:點選打開連結
2)BASH_ENV漏洞修複介紹:點選打開連結
六、fork、source和exec
shell程式設計的時候,往往不會把所有功能都寫在一個腳本中,這樣太不好維護了,需要多個腳本檔案協同工作。那麼問題來了,在一個腳本中怎麼調用其他的腳本呢?有三種方式,分别是fork、source和exec。
1. fork
fork其實最麻煩,他是從目前shell程序中fork一個子shell程序來完成調用,上文所述的所有情況都是fork調用形式,簡單總結,就是父shell程序的全局環境變量copy一份給子shell程序,因為是拷貝,是以子shell程序對這一份變量的所有操對父shell程序無影響,父程序的局部變量不會被子shell程序通路到;子shell的全局環境變量自對自己和自己的子shell程序有用,對父shell程序屏蔽,子shell的局部變量也隻對目前shell程序有效。
另外,fork調用其實就是在一個腳本中調用另一個腳本,被調用腳本執行完成之後傳回給父shell程序,父shell程序繼續執行剩下的指令,其中所涉及的慶幸上文已經基本全部覆寫,此處示範一下fork調用的示例代碼。
?
2. source
source調用,是把被調用腳本加載到目前的shell環境中來執行,就好像是在一個腳本裡面運作一樣,他們的定義的局部變量共享,在同一個程序中,如以下示例。
?
3. exec
exec調用,也是fork一個子shell環境來執行被調用腳本,但是父shell環境的執行權會被剝奪,也就是執行權被交給了被調用腳本,父shell環境不再擁有執行權,無論父shell腳本中的指令是否執行完成,都不在被執行,随着子shell程序的結束而結束。
關于fork、source和exec網上已經有很多介紹了,要是這裡沒有看明白,可以搜尋“fork、source和exec”。
轉載于:https://my.oschina.net/qingwuh/blog/881365