一 shell 的变量与shell执行方式,子shell
1.export 将一个变量变成环境变量
嚴格來說,我們在當前 shell 中所定義的變量,均屬於"本地變量"(local variable),
只有經過 export 命令的"輸出"處理,才能成為環境變量(environment variable):
$ A=B
$ export A
或
$ export A=B
經過 export 輸出處理之後,變量 A 就能成為一個環境變量供其後的命令及子进程(包括子shell)使用。
要理解這個 export ,事實上需要從 process 的角度來理解才能透徹。
首先,我們所執行的任何程式,都是由父行程(parent process)所產生出來的一個子行程(child process),
子行程在結束後,將返回到父行程去。此一現像在 Linux 系統中被稱為 fork 。
(為何要程為 fork 呢?嗯,畫一下圖或許比較好理解... ^_^ )
當子行程被產生的時候,將會從父行程那裡獲得一定的資源分配、及(更重要的是)繼承父行程的環境﹗
讓我們回到上一章所談到的"環境變量"吧:
* 所謂環境變量其實就是那些會傳給子行程的變量。
簡單而言,"遺傳性"就是區分本地變量與環境變量的決定性指標。
然而,從遺傳的角度來看,我們也不難發現環境變量的另一個重要特徵:
* 環境變量只能從父行程到子行程單向繼承。換句話說:在子行程中的環境如何變更,均不會影響父行程的環境。
2 子shell
接下來,再讓我們了解一下命令腳本(shell script)的概念。
所謂的 shell script 講起來很簡單,就是將你平時在 shell prompt 後所輸入的多行 command line 依序寫入一個文件去而已。
其中再加上一些條件判斷、互動界面、參數運用、函數調用等等技巧,得以讓 script 更加"聰明"的執行,
但若撇開這些技巧不談,我們真的可以簡單的看成 script 只不過依次執行預先寫好的命令行而已
再結合以上兩個概念(process + script),那應該就不難理解如下這句話的意思了:
* 正常來說,當我們執行一個 shell script 時,其實是先產生一個 sub-shell 的子行程,然後 sub-shell 再去產生命令行的子行程。
然則,那讓我們思考下面的例子:
a.sh脚本如下,里面调用b.sh
#!/bin/sh
echo "--start parent proc--"
echo $PWD
echo "--parent start child proc--"
sh b.sh
echo "--return to parent proc--"
echo $PWD
b.sh脚本如下
#!/bin/sh
echo $PWD
cd /home
echo $PWD
调用结果
[root@test 1029]# sh a.sh
--start parent proc--
/root/1029
--parent start child proc--
/root/1029
/home
--return to parent proc--
/root/1029
因為,一般我們跑的 shell script 是用 subshell 去執行的。
從 process 的觀念來看,是 parent process 產生一個 child process 去執行,
當 child 結束後,會返回 parent ,但 parent 的環境是不會因 child 的改變而改變的。
所謂的環境元數很多,凡舉 effective id, variable, workding dir 等等...
其中的 workding dir ($PWD) 正是樓主的疑問所在:
當用 subshell 來跑 script 的話,sub shell 的 $PWD 會因為 cd 而變更,
但當返回 primary shell 時,$PWD 是不會變更的。
3.shell执行方式(sh 脚本或source 脚本)
當你有了 fork 的概念之後,要理解 source 就不難:
* 所謂 source 就是讓 script 在當前 shell 內執行、而不是產生一個 sub-shell 來執行。 调用方式是 source script-file或者 . script-file
由於所有執行結果均於當前 shell 內完成,若 script 的環境有所改變(包括export新的环境变量),當然也會改變當前環境了(脚本里面export的环境变量导入到当前环境)﹗
例如单独执行b.sh
如果直接执行
[root@test 1029]# sh b.sh
/root/1029
/home
[root@test 1029]#
执行完后仍然在当前目录1029下
如果用source执行
[root@test 1029]# source b.sh
/root/1029
/home
[root@test home]#
执行完后就到目录/home里面去了
二 login shell與 non-login shell
1 概念
* login shell:取得 bash 時需要完整的登入流程的,就稱為 login shell。舉例來說,你要由 tty1 ~ tty6 登入,需要輸入使用者的帳號與密碼,此時取得的 bash 就稱為『 login shell 』囉;
* non-login shell:取得 bash 介面的方法不需要重複登入的舉動,舉例來說,(1)你以 X window 登入 Linux 後, 再以 X 的圖形化介面啟動終端機,此時那個終端介面並沒有需要再次的輸入帳號與密碼,那個 bash 的環境就稱為 non-login shell了。(2)你在原本的 bash 環境下再次下達 bash 這個指令,同樣的也沒有輸入帳號密碼, 那第二個 bash (子程序) 也是 non-login shell 。
2 读取的文件
這兩個取得 bash 的情況中,讀取的設定檔資料並不一樣所致。 由於我們需要登入系統,所以先談談 login shell 會讀取哪些設定檔?一般來說,login shell 其實只會讀取這兩個設定檔:
1. /etc/profile:這是系統整體的設定,你最好不要修改這個檔案;
2. ~/.bash_profile 或 ~/.bash_login 或 ~/.profile:屬於使用者個人設定,你要改自己的資料,就寫入這裡!
2.1 /etc/profile (login shell 才會讀)
這個檔案設定的變數主要有:
* PATH:會依據 UID 決定 PATH 變數要不要含有 sbin 的系統指令目錄;
* MAIL:依據帳號設定好使用者的 mailbox 到 /var/spool/mail/帳號名;
* USER:根據使用者的帳號設定此一變數內容;
* HOSTNAME:依據主機的 hostname 指令決定此一變數內容;
* HISTSIZE:歷史命令記錄筆數。CentOS 5.x 設定為 1000 ;
/etc/profile 可不止會做這些事而已,他還會去呼叫外部的設定資料喔!在 CentOS 5.x 預設的情況下, 底下這些資料會依序的被呼叫進來:
/etc/inputrc
/etc/profile.d/*.sh
/etc/sysconfig/i18n
2.2 ~/.bash_profile (login shell 才會讀)
bash 在讀完了整體環境設定的 /etc/profile 並藉此呼叫其他設定檔後,接下來則是會讀取使用者的個人設定檔。 在 login shell 的 bash 環境中,所讀取的個人偏好設定檔其實主要有三個,依序分別是:
1. ~/.bash_profile
2. ~/.bash_login
3. ~/.profile
其實 bash 的 login shell 設定只會讀取上面三個檔案的其中一個, 而讀取的順序則是依照上面的順序。也就是說,如果 ~/.bash_profile 存在,那麼其他兩個檔案不論有無存在,都不會被讀取。
2.3 ~/.bashrc (non-login shell 會讀)
[root@www ~]# cat ~/.bashrc
# .bashrc
# User specific aliases and functions
alias rm='rm -i' <==使用者的個人設定
alias cp='cp -i'
alias mv='mv -i'
# Source global definitions
if [ -f /etc/bashrc ]; then <==整體的環境設定
. /etc/bashrc
fi
常见问题
如果我们在/etc/profile里面定义了JAVA_HOME,然后写了java的启动脚本,常常会发现直接执行脚本可以,但是放入到crontab里面就不行(因为crontab里面的是在non-login shell下执行的,不会读取/etc/profile文件,自然找不到$JAVA_HOME
这个时候crontab这么写
su - root 脚本
本文转自yahoon 51CTO博客,原文链接:http://blog.51cto.com/yahoon/412439,如需转载请自行联系原作者