SHELL十三問之六:exec 跟 source 差在哪?

這次先讓我們從CU Shell版的一個實例貼子來談起吧:
/T:]4p3I1
例中的提問是:BSD愛好者樂園M I�j x8lzp^?

cd /etc/aa/bb/cc可以執行,但是把這條命令寫入shellshell不執行!BSD愛好者樂園;u}B'je.MU0@|
這是什麼原因呀!

8O]'WyE7r6~1`1

我當時如何回答暫時別去深究,先讓我們瞭解一下進程(process)的觀念好了。
;Jv#/,gy1BSD愛好者樂園/Jrq/SYs /
首先,我們所執行的任何程序,都是由父進程(parent process)所產生出來的一個子進程(child process),子進程在結束後,將返回到父進程去。此一現像在Linux系統中被稱爲 forkBSD愛好者樂園 X,x+z6?-nO$q;l
當子進程被產生的時候,將會從父進程那裏獲得一定的資源分配、及(更重要的是)繼承父進程的環境BSD愛好者樂園,W q ea8w1H%R
BSD愛好者樂園p/ZFl,T3w5d
讓我們回到上一章所談到的"環境變量"吧:
4X p:eQ4E |1
*所謂環境變量其實就是那些會傳給子進程的變量。BSD愛好者樂園i q-c�` v/3b'u
簡單而言,"遺傳性"就是區分本地變量與環境變量的決定性指標。
2vw?&I5bo&N1BSD愛好者樂園/[;Kh/!k
然而,從遺傳的角度來看,我們也不難發現環境變量的另一個重要特徵:BSD愛好者樂園R�SO7C3{|h(f u#d_
*
環境變量只能從父進程到子進程單向繼承。換句話說:在子進程中的環境如何變更,均不會影響父進程的環境。BSD愛好者樂園B T6Vr'S#ZJ�aiF
BSD愛好者樂園-Ks"Pi&`/@bc],zDc
接下來,再讓我們瞭解一下命令腳本(shell script)的概念。
9t Bn3t|1
所謂的shell script講起來很簡單,就是將你平時在shell prompt後所輸入的多行command line依序寫入一個文件去而已。
5PC~?"dT sF1
其中再加上一些條件判斷、互動界面、參數運用、函數調用、等等技巧,得以讓script更加"聰明"的執行,但若撇開這些技巧不談,我們真的可以簡單的看成script只不過依次執行預先寫好的命令行而已
#r*xIA!DFB1BSD愛好者樂園 o'v U7sn|%Kz
再結合以上兩個概念(process + script),那應該就不難理解如下這句話的意思了:BSD愛好者樂園E V/C u&Jn
*
正常來說,當我們執行一個shell script時,其實是先產生一個sub-shell的子進程,然後sub-shell再去產生命令行的子進程。BSD愛好者樂園r.T;?K8I i!B*I
然則,那讓我們回到本章開始時所提到的例子再從新思考:

qW`%q/S8^1

cd /etc/aa/bb/cc可以執行,但是把這條命令寫入shellshell不執行!BSD愛好者樂園7` p;r4wr6l.ziV
這是什麼原因呀!

o.hk'Xy.Z;b/r1

我當時的答案是這樣的:BSD愛好者樂園1V$BS:}f7O5| xC

因爲,一般我們跑的shell script是用subshell去執行的。BSD愛好者樂園/Q th.I7p#hRbA
process的觀念來看,是parent process產生一個child process去執行,當child結束後,會返回parent,但parent的環境是不會因child的改變而改變的。
8U6~eq `2G _0Jg1
所謂的環境元數很多,凡舉effective id, variable, workding dir等等...BSD愛好者樂園Y1I([8O+P9B
其中的workding dir ($PWD)正是樓主的疑問所在:BSD愛好者樂園IP6["YZ/2M
當用subshell來跑script的話,sub shell$PWD會因爲cd而變更,
5UBw1u f4P I A R1
但當返回primary shell時,$PWD是不會變更的。BSD愛好者樂園%[ f0Kj1e,f3p:v

能夠了解問題的原因及其原理是很好的,但是?如何解決問題恐怕是我們更感興趣的﹗是吧?^_^BSD愛好者樂園t,f#/g&E1RdPYm

J)[*?h8Hg1d0`1
那好,接下來,再讓我們瞭解一下source命令好了。
g#O(W'z{Z1
當你有了fork的概念之後,要理解source就不難:
C/QS@p$Dtp1
*所謂source就是讓script在當前shell內執行、而不是產生一個sub-shell來執行。
4Di}H|)[email protected]
由於所有執行結果均於當前shell內完成,若script的環境有所改變,當然也會改變當前環境了﹗
)/h'bc5Q-[5x1
因此,只要我們要將原本單獨輸入的script命令行變成source命令的參數,就可輕易解決前例提到的問題了。
2tI c T:|'^!I1
比方說,原本我們是如此執行  script的:

Q(WSX /"V5Rm1

./my.script

bS:k]f2lIB1

現在改成這樣即可:BSD愛好者樂園S5T*]&WzaZ5Vr[

source ./my.script
'Ue(Whb kc4m�f(f1
或:BSD愛好者樂園R!Z]b2D%i#c[
. ./my.script

sgk'j#i `7vp1

說到這裏,我想,各位有興趣看看/etc底下的衆多設定文件,應該不難理解它們被定義後,如何讓其它script讀取並繼承了吧?
6}`Q{^i1
若然,日後你有機會寫自己的script,應也不難專門指定一個設定文件以供不同的script一起"共享"...  ^_^BSD愛好者樂園8N0WE&n)ux
BSD愛好者樂園9q w P_#p ^2r.JF/]%@
okay
,到這裏,若你搞得懂forksource的不同,那接下來再接受一個挑戰:BSD愛好者樂園G*T?Tg1s/d~
----
exec又與source/fork有何不同呢?BSD愛好者樂園*vn3] S-}B
...要了解exec或許較爲複雜,尤其扯上File Descriptor的話...
|"sJ&vi&E$y1
mk*c$a!jY sU L1
不過,簡單來說:BSD愛好者樂園%U/Z {0~5b
* exec也是讓script在同一個進程上執行,但是原有進程則被結束了
W p$v&L N$fEo2SD2x!]1
也就是簡而言之:原有進程會否終止,就是execsource/fork的最大差異了。BSD愛好者樂園t^(e_v'd2k
BSD愛好者樂園'v0_$^o W
嗯,光是從理論去理解,或許沒那麼好消化,不如動手"實作+思考"來的印像深刻哦。BSD愛好者樂園�U&b/d0z {f
下面讓我們寫兩個簡單的script,分別命令爲1.sh2.shBSD愛好者樂園MF2j3eBv u0F&N,?
BSD愛好者樂園/_|/`R-dfF*B
1.sh

b/QD6~v S+IM1

#!/bin/shBSD愛好者樂園R/l A.{ SOE�Mrf
A=BBSD愛好者樂園_ z,}3@jM
echo "PID for 1.sh before exec/source/fork:$$"BSD愛好者樂園"hqq x'K0t#LcN&j8w
export A
rX%|&Zr1echo "1.sh: /$A is $A"
q[{t I0m'k1case $1 inBSD愛好者樂園9ndQ_2J
        exec)
9yd-L'Y?m1                echo "using exec..."
Td3d/U|1                exec ./2.sh ;;
r@"^F/y~X1R*R;U3X1        source)
CO4~7XK~|:H.l1                echo "using source..."BSD愛好者樂園Ng!w;_[ t x
                . ./2.sh ;;
!V#s P0F3xz1        *)
wB]2I-N!r1                echo "using fork by default..."
%U"e+~xh U Q1                ./2.sh ;;BSD愛好者樂園3~ zB+p;W
esac
Q aW0Mn6a1echo "PID for 1.sh after exec/source/fork:$$"BSD愛好者樂園9J g:o"Ty!w+a G s9y
echo "1.sh: /$A is $A"
BSD愛好者樂園h9rB5X"v }

2.sh

B g6E5k!f-|b Jy1

#!/bin/shBSD愛好者樂園%] b0XP.B8F2U
echo "PID for 2.sh: $$"BSD愛好者樂園m0tI!L#p{8AI
echo "2.sh get /$A=$A from 1.sh"BSD愛好者樂園:C xi;w?IFL
A=CBSD愛好者樂園 js4G7[Jn/,r;/
export ABSD愛好者樂園+R-kSH0C0Qm6M%K&}
echo "2.sh: /$A is $A"
BSD愛好者樂園Y4uf/E:SN3P2c

然後,分別跑如下參數來觀察結果:

G%[$b9C,uR3b_O aw1

$ ./1.sh fork

6dj1mw0NH,`1

PID for 1.sh before exec/source/fork:531

Nm�R PWh0p:k1

1.sh: $A is BBSD愛好者樂園(D4[(c6hO/+t^&JI

using fork by default...BSD愛好者樂園t8x b�aN"P+^&q|

PID for 2.sh:532

5OA eyo1

2.sh get $A=B from 1.shBSD愛好者樂園OL.Kd h4W(h

2.sh: $A is CBSD愛好者樂園^`T8s z(z&pG

PID for 1.sh after exec/source/fork:531

n4K2p7y A?!N1

1.sh: $A is B

:z b op[$a,bN1

 BSD愛好者樂園 KS)}1_x8x

$ ./1.sh source

5dB7C.Q!Rh!L1

PID for 1.sh before exec/source/fork:533

'Q(B,t-kQ2@5z1

1.sh: $A is BBSD愛好者樂園-|-_B!v@|3y R x O

using source...BSD愛好者樂園-R%~ aKz

PID for 2.sh:533

-N.W_�JN:h1

2.sh get $A=B from 1.sh

5w"s+A_y}e�D~1

2.sh: $A is C

'y SSL2q1

PID for 1.sh after exec/source/fork:533

)JH4G F,K(t4O1

1.sh: $A is C

7U5o ] Q1|3}1

 BSD愛好者樂園`TE OI#iSp�t z

$ ./1.sh exec

Pq%q|`3X1

PID for 1.sh before exec/source/fork:537BSD愛好者樂園6QK:|:u%an:x,[P

1.sh: $A is BBSD愛好者樂園Y,Qc(Ig D v w1q6H5iJ

using exec...BSD愛好者樂園O;H$k q6pqM8D'jH"r

PID for 2.sh:537BSD愛好者樂園f em4k5E5nk*k(FX

2.sh get $A=B from 1.shBSD愛好者樂園h4D/Cp+WA`

2.sh: $A is C

RTK9PD|X;V:eq1

############BSD愛好者樂園d3@W$b l

echo "PID for 1.sh after exec/source/fork:$$"
3jV_B-t.~Al"i1echo "1.sh: /$A is $A"
BSD愛好者樂園1X!F)`+E$K+OMk

已經不會執行了,1.sh的進程已經沒了。

yn7qoV,w:M8bI K_1

##############

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章