[shell問答錄]:命令、程式、子shell...(轉)
[shell問答錄]:命令、程式、子shell...(轉)[@more@]作者:woodie
前些天在CU上討論一個統計正在執行的指令碼數量的問題過程中,發現自己對於shell如何執行命令方面瞭解還是甚少,慚愧慚愧...期間得到waker兄的指點,在此表示感謝!他的說法除了個別地方不太準確外,基本上是正確的。這些天抽時間找了些資料研究了一下,又學到了不少!這裡把我的一點心得以問答的形式貼出來,供大家參考。小弟才疏學淺,錯誤的地方一定很多,歡迎大家拍磚、指正!
Q1: shell如何執行“簡單”命令?
A: 這裡的簡單命令和bash參考手冊裡的含義相同,形式上一般是:命令的名稱加上它的引數。有三種不同的簡單命令:
1.內建命令(builtin)
是shell解釋程式內建的,有shell直接執行,不需要派生新的程式。有一些內部命令可以用來改變當前的shell環境,如:
cd /path
var=value
read var
export var
...
2.外部命令("external command" or "disk command")
二進位制可執行檔案,需要由磁碟裝入記憶體執行。會派生新的程式,shell解釋程式會呼叫fork自身的一個複製,然後用exec系列函式來執行外部命令,然後外部命令就取代了先前fork的子shell。
3.shell指令碼(script)
shell解釋程式會fork+exec執行這個指令碼命令,在exec呼叫中核心會檢查指令碼的第一行(如:#!/bin/sh),找到用來執行指令碼的解釋程式,然後裝入這個解釋程式,由它解釋執行指令碼程式。解釋程式可能有很多種,各種shell(Bourne shell,Korn shell cshell,rc及其變體ash,dash,bash,zshell,pdksh,tcsh,es...),awk,tcl/tk,expect,perl,python,等等。在此解釋程式顯然是當前shell的子程式。如果這個解釋程式與當前使用的shell是同一種shell,比如都是bash,那麼它就是當前shell的子shell,指令碼中的命令都是在子shell環境中執行的,不會影響當前shell的環境。
Q2: shell指令碼是否作為單獨的一個程式執行?
A: 不是,shell指令碼本身不能作為一個程式。如上面講的,shell指令碼由一個shell解釋程式來解釋、執行其中的命令。這個shell解釋程式是單獨的一個程式,指令碼中的外部命令也都作為獨立程式依次被執行。這也就是為什麼ps不能找到正在執行的指令碼的名字的原因了。作為一個替代方案,你可以這樣呼叫指令碼:
sh script-name
這時shell解釋程式“sh”作為一個外部命令被顯式地呼叫,而script-name作為該命令的命令列引數可以被我們ps到。
另外,如果你的系統上有pidof命令可用,它倒是可以找出shell指令碼程式(實際上應該是執行shell指令碼的子shell程式)的程式ID:
pidof -x script-name
Q3: shell何時在子shell中執行命令?
A: 在此我們主要討論Bourne shell及其相容shell。在許多情況下shell會在子shell中執行命令:
1.(...)結構
小括號內的命令會在一個子shell環境中執行,命令執行的結果不會影響當前的shell環境。需要注意是此時變數$$會顯示當前shell的程式id,而不是子shell的程式id。
參考:
{...;}結構中的命令在當前shell中執行,(內部)命令執行的結果會影響當前的shell環境。
2.後臺執行或非同步執行
command&
命令由一個子shell在後臺執行,當前shell立即取得控制等候使用者輸入。後臺命令和當前shell的執行是並行的,但沒有互相的依賴、等待關係,所以是非同步的並行。
3.命令替換
`command`(Bourn shell及相容shell/csh)
$(command)(在ksh/bash/zsh中可用)
將command命令執行的標準輸出代換到當前的命令列。command在子shell環境中執行,結果不會影響當前的shell環境。
4.管道(不同的shell處理不同)
cmd1|cmd2
cmd1和cmd2並行執行,並且相互有依賴關係,cmd2的標準輸入來自cmd1的標準輸出,二者是“同步”的。
對管道的處理不同的shell實現的方式不同。
在linux環境下大多數shell(bash/pdksh/ash/dash等,除了zshell例外)都將管道中所有的命令在子shell環境中執行,命令執行的結果不會影響當前的shell環境。
Korn shell的較新的版本(ksh93以後)比較特殊,管道最後一級的命令是在當前shell執行的。這是一個feature而非BUG,在POSIX標準中也是允許的。這樣就使下面的命令結構成為可能:
command|read var
由於read var(read是一個內部命令)在當前shell中執行,var的值在當前shell就是可用的。
反之bash/pdksh/ash/dash中read var在子shell環境中執行,var讀到的值無法傳遞到當前shell,所以變數var無法取得期望的值。類似這樣的問題在各種論壇和news group中經常被問到。個人認為command|read var的結構很清晰,並且合乎邏輯,所以我認為Korn shell的這個feature很不錯。可惜不是所有的shell都是這樣實現的。:(如開源的pdksh就是在子shell執行管道的每一級命令。
Korn shell對管道的處理還有一個特殊的地方,就是管道如果在後臺執行的話,管道前面的命令會由最後一級的命令派生,而不是由當前shell派生出來。據說Bourne shell也有這個特點(標準的Bourne shell沒有測試環境,感興趣的朋友有條件的可以自行驗證)。但是他們的開源模仿者,pdksh和ash卻不是這樣處理。
最特殊的是zshell,比較新的zshell實現(好像至少3.0.5以上)會在當前shell中執行管道中的每一級命令,不僅僅是最後一條。每一條命令都由當前shell派生,在後臺執行時也是一樣。可見在子sehll中執行管道命令並不是不得已的做法,大概只是因為實現上比較方便或者這樣的處理已經成為unix的傳統之一了吧。;-)
讓我們總結一下,不同的shell對管道命令的處理可能不同。有的shell中command|read var這樣的結構是ok的,但我們的程式碼出於相容性的緣故不能依賴這一點,最好能避免類似的程式碼。
5.程式替換(僅bash/zsh中,非POSIX相容)
>(...)
與管道有點類似,例子:cmd1 (cmd3), cmd1, cmd2, cmd3的執行是同步並行的。
>(command)形式可以用在任何命令列中需要填寫輸出檔案的地方,該命令的輸出會被command作為標準輸入讀入。
兩種形式中的command都在子shell環境中執行,結果不會影響當前的shell環境。
6.if或while命令塊的輸入輸出重定向
在SVR4.2的Bourne shell中對此情況會fork一個子shell執行if塊和while塊中的命令;在linux下似乎其它的shell中都不這樣處理。
7.協程式(ksh)
只有Korn shell和pdksh有協程式的機制(其它shell中可以用命名管道來模擬)。類似於普通的後臺命令,協程式在後臺同步執行,所以必須在子shell中執行。協程式與後臺命令不同的是它要和前臺程式(使用read -p和print -p)進行互動,而後者一般只是簡單地非同步執行。
Q4: 既然在當前shell中執行命令也會派生子shell,那麼它與在子shell中執行命令又有什麼區別呢?
A: 這種說法不準確。
在當前shell中執行內部命令不會派生子shell,因此有些內部命令才能夠改變當前的shell執行環境。
在當前shell中執行外部命令或指令碼時會派生子shell,所以這時命令的執行不會影響當前 的shell環境。注意:子shell中執行的內部命令只會改變子shell的執行環境,而不會改變當前shell(父shell)的環境。
Q5: 怎樣把子shell中的變數傳回父shell?
A: 例如(echo "$a") | read b不能工作,如何找到一個替代方案?下面給出一些可能的方案:
1.使用臨時檔案
...
#in subshell
a=100
echo "$a">tmpfile
...
#in parent
read b
2.使用命名管道
mkfifo pipef
(...
echo "$a" > pipef
...)
read b
3.使用coprocess(ksh)
( echo "$a" |&)
read -p b
4.使用命令替換
b=`echo "$a"`
5.使用eval命令
eval `echo "b=$a"`
6.使用here document
read b < `echo "$a"`
END
7.使用here string(bash/pdksh)
read b <<
8.不用子shell,用.命令或source命令執行指令碼。
即在當前shell環境下執行指令碼,沒有子shell,也就沒有了子shell的煩惱。:)
解決的方法還不止於此,其它的程式間通訊手段應該也能使用,這有待於大家一起發掘了。
前些天在CU上討論一個統計正在執行的指令碼數量的問題過程中,發現自己對於shell如何執行命令方面瞭解還是甚少,慚愧慚愧...期間得到waker兄的指點,在此表示感謝!他的說法除了個別地方不太準確外,基本上是正確的。這些天抽時間找了些資料研究了一下,又學到了不少!這裡把我的一點心得以問答的形式貼出來,供大家參考。小弟才疏學淺,錯誤的地方一定很多,歡迎大家拍磚、指正!
Q1: shell如何執行“簡單”命令?
A: 這裡的簡單命令和bash參考手冊裡的含義相同,形式上一般是:命令的名稱加上它的引數。有三種不同的簡單命令:
1.內建命令(builtin)
是shell解釋程式內建的,有shell直接執行,不需要派生新的程式。有一些內部命令可以用來改變當前的shell環境,如:
cd /path
var=value
read var
export var
...
2.外部命令("external command" or "disk command")
二進位制可執行檔案,需要由磁碟裝入記憶體執行。會派生新的程式,shell解釋程式會呼叫fork自身的一個複製,然後用exec系列函式來執行外部命令,然後外部命令就取代了先前fork的子shell。
3.shell指令碼(script)
shell解釋程式會fork+exec執行這個指令碼命令,在exec呼叫中核心會檢查指令碼的第一行(如:#!/bin/sh),找到用來執行指令碼的解釋程式,然後裝入這個解釋程式,由它解釋執行指令碼程式。解釋程式可能有很多種,各種shell(Bourne shell,Korn shell cshell,rc及其變體ash,dash,bash,zshell,pdksh,tcsh,es...),awk,tcl/tk,expect,perl,python,等等。在此解釋程式顯然是當前shell的子程式。如果這個解釋程式與當前使用的shell是同一種shell,比如都是bash,那麼它就是當前shell的子shell,指令碼中的命令都是在子shell環境中執行的,不會影響當前shell的環境。
Q2: shell指令碼是否作為單獨的一個程式執行?
A: 不是,shell指令碼本身不能作為一個程式。如上面講的,shell指令碼由一個shell解釋程式來解釋、執行其中的命令。這個shell解釋程式是單獨的一個程式,指令碼中的外部命令也都作為獨立程式依次被執行。這也就是為什麼ps不能找到正在執行的指令碼的名字的原因了。作為一個替代方案,你可以這樣呼叫指令碼:
sh script-name
這時shell解釋程式“sh”作為一個外部命令被顯式地呼叫,而script-name作為該命令的命令列引數可以被我們ps到。
另外,如果你的系統上有pidof命令可用,它倒是可以找出shell指令碼程式(實際上應該是執行shell指令碼的子shell程式)的程式ID:
pidof -x script-name
Q3: shell何時在子shell中執行命令?
A: 在此我們主要討論Bourne shell及其相容shell。在許多情況下shell會在子shell中執行命令:
1.(...)結構
小括號內的命令會在一個子shell環境中執行,命令執行的結果不會影響當前的shell環境。需要注意是此時變數$$會顯示當前shell的程式id,而不是子shell的程式id。
參考:
{...;}結構中的命令在當前shell中執行,(內部)命令執行的結果會影響當前的shell環境。
2.後臺執行或非同步執行
command&
命令由一個子shell在後臺執行,當前shell立即取得控制等候使用者輸入。後臺命令和當前shell的執行是並行的,但沒有互相的依賴、等待關係,所以是非同步的並行。
3.命令替換
`command`(Bourn shell及相容shell/csh)
$(command)(在ksh/bash/zsh中可用)
將command命令執行的標準輸出代換到當前的命令列。command在子shell環境中執行,結果不會影響當前的shell環境。
4.管道(不同的shell處理不同)
cmd1|cmd2
cmd1和cmd2並行執行,並且相互有依賴關係,cmd2的標準輸入來自cmd1的標準輸出,二者是“同步”的。
對管道的處理不同的shell實現的方式不同。
在linux環境下大多數shell(bash/pdksh/ash/dash等,除了zshell例外)都將管道中所有的命令在子shell環境中執行,命令執行的結果不會影響當前的shell環境。
Korn shell的較新的版本(ksh93以後)比較特殊,管道最後一級的命令是在當前shell執行的。這是一個feature而非BUG,在POSIX標準中也是允許的。這樣就使下面的命令結構成為可能:
command|read var
由於read var(read是一個內部命令)在當前shell中執行,var的值在當前shell就是可用的。
反之bash/pdksh/ash/dash中read var在子shell環境中執行,var讀到的值無法傳遞到當前shell,所以變數var無法取得期望的值。類似這樣的問題在各種論壇和news group中經常被問到。個人認為command|read var的結構很清晰,並且合乎邏輯,所以我認為Korn shell的這個feature很不錯。可惜不是所有的shell都是這樣實現的。:(如開源的pdksh就是在子shell執行管道的每一級命令。
Korn shell對管道的處理還有一個特殊的地方,就是管道如果在後臺執行的話,管道前面的命令會由最後一級的命令派生,而不是由當前shell派生出來。據說Bourne shell也有這個特點(標準的Bourne shell沒有測試環境,感興趣的朋友有條件的可以自行驗證)。但是他們的開源模仿者,pdksh和ash卻不是這樣處理。
最特殊的是zshell,比較新的zshell實現(好像至少3.0.5以上)會在當前shell中執行管道中的每一級命令,不僅僅是最後一條。每一條命令都由當前shell派生,在後臺執行時也是一樣。可見在子sehll中執行管道命令並不是不得已的做法,大概只是因為實現上比較方便或者這樣的處理已經成為unix的傳統之一了吧。;-)
讓我們總結一下,不同的shell對管道命令的處理可能不同。有的shell中command|read var這樣的結構是ok的,但我們的程式碼出於相容性的緣故不能依賴這一點,最好能避免類似的程式碼。
5.程式替換(僅bash/zsh中,非POSIX相容)
>(...)
與管道有點類似,例子:cmd1 (cmd3), cmd1, cmd2, cmd3的執行是同步並行的。
>(command)形式可以用在任何命令列中需要填寫輸出檔案的地方,該命令的輸出會被command作為標準輸入讀入。
兩種形式中的command都在子shell環境中執行,結果不會影響當前的shell環境。
6.if或while命令塊的輸入輸出重定向
在SVR4.2的Bourne shell中對此情況會fork一個子shell執行if塊和while塊中的命令;在linux下似乎其它的shell中都不這樣處理。
7.協程式(ksh)
只有Korn shell和pdksh有協程式的機制(其它shell中可以用命名管道來模擬)。類似於普通的後臺命令,協程式在後臺同步執行,所以必須在子shell中執行。協程式與後臺命令不同的是它要和前臺程式(使用read -p和print -p)進行互動,而後者一般只是簡單地非同步執行。
Q4: 既然在當前shell中執行命令也會派生子shell,那麼它與在子shell中執行命令又有什麼區別呢?
A: 這種說法不準確。
在當前shell中執行內部命令不會派生子shell,因此有些內部命令才能夠改變當前的shell執行環境。
在當前shell中執行外部命令或指令碼時會派生子shell,所以這時命令的執行不會影響當前 的shell環境。注意:子shell中執行的內部命令只會改變子shell的執行環境,而不會改變當前shell(父shell)的環境。
Q5: 怎樣把子shell中的變數傳回父shell?
A: 例如(echo "$a") | read b不能工作,如何找到一個替代方案?下面給出一些可能的方案:
1.使用臨時檔案
...
#in subshell
a=100
echo "$a">tmpfile
...
#in parent
read b
2.使用命名管道
mkfifo pipef
(...
echo "$a" > pipef
...)
read b
3.使用coprocess(ksh)
( echo "$a" |&)
read -p b
4.使用命令替換
b=`echo "$a"`
5.使用eval命令
eval `echo "b=$a"`
6.使用here document
read b <
END
7.使用here string(bash/pdksh)
read b <<
8.不用子shell,用.命令或source命令執行指令碼。
即在當前shell環境下執行指令碼,沒有子shell,也就沒有了子shell的煩惱。:)
解決的方法還不止於此,其它的程式間通訊手段應該也能使用,這有待於大家一起發掘了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-952477/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Linux 作業系統程式設計之Shell 問答錄(轉)Linux作業系統程式設計
- Unix程式設計/應用問答中文版 ---20.shell script問題(轉)程式設計
- Linux Shell命令換行問題(轉)Linux
- shell的命令(轉)
- 關於webpack問答記錄...Web
- QTP問與答(轉)QT
- C#問答 (轉)C#
- fish shell 自動補全子命令
- Linux Shell程式設計(27)——子shellLinux程式設計
- 20條Linux命令面試問答Linux面試
- 防破解問答集 (轉)
- XHTML基礎問答(轉)HTML
- Shell程式設計-11-子Shell和Shell巢狀程式設計巢狀
- Linux問與答(1)(轉)Linux
- Linux應用問答(轉)Linux
- 面試問答詳解(一) (轉)面試
- 面試問答詳解(三) (轉)面試
- 面試問答詳解(二) (轉)面試
- Linux Shell之sort命令(轉)Linux
- shell中set命令詳解(轉)
- BASH shell set命令詳解(轉)
- 快速編輯 Shell 命令列(轉)命令列
- Linux問與答:KDE問題解凝(轉)Linux
- Windows 2000 高階問答 轉 (轉)Windows
- Bourne Shell及shell程式設計(轉)程式設計
- Linux桌面應用問答(轉)Linux
- 50個LINUX問答題(轉)Linux
- Hadoop Shell命令 |HDFS Shell命令| HDFS 命令Hadoop
- Shell程式設計-read命令程式設計
- shell程式設計(轉)程式設計
- 防火牆常見問題十問十答(轉)防火牆
- Linux網路應用問答(轉)Linux
- Word軟體疑難故障問答(轉)
- Shell命令
- Linux快速編輯Shell命令列(轉)Linux命令列
- 在shell中捕捉訊號的命令(轉)
- Unix程式設計/應用問答中文版 ---19.終端相關問題(轉)程式設計
- 設工程專案管理規範問答(轉)專案管理