了解一下-shell的变量与执行

 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 呢?嗯,畫一下圖或許比較好理解...  ^_^ )

當子行程被產生的時候,將會從父行程那裡獲得一定的資源分配、及(更重要的是)繼承父行程的環境﹗

讓我們回到上一章所談到的"環境變量"吧:

所謂環境變量其實就是那些會傳給子行程的變量。

簡單而言,"遺傳性"就是區分本地變量與環境變量的決定性指標。

然而,從遺傳的角度來看,我們也不難發現環境變量的另一個重要特徵:

環境變量只能從父行程到子行程單向繼承。換句話說:在子行程中的環境如何變更,均不會影響父行程的環境。

 

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

概念 

  *  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 

 

 

读取的文件

這兩個取得 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,如需转载请自行联系原作者

 

上一篇:TypeScript constructor signature 类型的变量赋值方式


下一篇:动手实战-基础学习之Docker镜像管理快速入门.笔记