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,perlpython,等等。在此解釋程序顯然是當前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 <(cmd2) >(cmd3), cmd1, cmd2, cmd3的執行是同步並行的。
<(command)形式可以用在任何命令行中需要填寫輸入文件名的地方,command的標准輸出會被該命令當作一個輸入文件讀入。
>(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<tmpfile
2.使用命名管道
mkfifo pipef
(...
echo "$a" > pipef
...)
read b <pipef
3.使用coprocess(ksh)
( echo "$a" |&)
read -p b
4.使用命令替換
b=`echo "$a"`
5.使用eval命令
eval `echo "b=$a"`
6.使用here document
read b <<END
`echo "$a"`
END
7.使用here string(bash/pdksh)
read b <<<`echo "$a"`
8.不用子shell,用.命令或source命令執行腳本。
即在當前shell環境下執行腳本,沒有子shell,也就沒有了子shell的煩惱。:)
更多相關文章
  • strace前言:strace常用來跟蹤進程執行時的系統調用的所接受的信號.在linux世界,進程是不能直接訪問硬件設備,當進程需要訪問硬件(比如讀取磁盤文件,接收網路數據等等)時,必須由用戶態模式切換至內核態模式,通過系統調用訪問硬件設備.strace可以跟蹤到一個進程産生的系統調用,包括參數,返 ...
  • 進程前言:進程指的是執行中程序的一個實例.新進程由fork()與execve()等系統調用所起始,然後執行,知道他們下達exit()系統調用爲止.linux系統都支持多進程.雖然計算機看起來像是一次做了很多事,但除非是他擁有多個CPU,否則一次做了好多事只是個錯覺.事實上,每個進程僅容許在一個極短的 ...
  • 今天登錄到伺服器上時,系統列印有6 zombie processes存在,于是用kill -9去清理掉這些僵屍進程,命令執行完後沒有錯誤,可是再次查找時,發現僵屍進程仍然存在,不知道怎麽清理了,上網找了一下,學習一下. 僵屍進程定義與查找 在UNIX 系統中,一個進程結束了,但是他的父進程沒有等待( ...
  • 下面是一個Shell腳本,用于管理Redis進程(啓動,停止,重啓),這個腳本可供參考.#!/bin/sh## redis - this script starts and stops the redis-server daemon## chkconfig:   - 85 15# descripti ...
  • #!/bin/bashwhile [ 1 ]doSendMail_is_exstit=$(ps -ef | grep "feed SendMail" | grep -v grep | wc -l)if [ ${SendMail_is_exstit} == 0 ]thencd /v ...
  • 實例解析shell子進程(subshell )   通過實例,解析個人對shell子進程的一個了解,主要包括以下幾個方面 1:什麽是shell子進程 2:shell什麽情況下會産生子進程 3:子進程的特點與注意事項 4:$變量$$在腳本裏的意義,及如何得到子進程裏的進程號   參考文檔:apue,b ...
  • 終端的問題涉及幾個概念,那就是進程組,會話,作業,下面會分別進行介紹.會話包含了一系列的進程,這些進程按照不同的執行內容會組織成若幹進程組,一個會話內的所有進程都必須是該會話首長進程的後代,這樣就保證了這些進程都是由該會話首長進程直接或者間接開啓的,只有這樣的才能保證這些進程確實是在會話首長進程耳目 ...
  • subprocess--子進程管理器 一.subprocess 模塊簡介 subprocess最早是在2.4版本中引入的.subprocess模塊用來生成子進程,並可以通過管道連接它們的輸入/輸出/錯誤,以及獲得它們的返回值.它用來代替多個舊模塊和函數:os.systemos.spawn*os.po ...
一周排行
  •     這篇是承接<輕量級 Java 開發框架 設計>系列Blog文的後續文章,本文將介紹 Hasor 內部主要插件並附帶插件的功能描述和簡單使用 Demo.     Hasor 使用的是"微內 ...
  • java提供的Arraylist本身不能對添加的元素進行去重,需要在添加後進行比較,如果相同就不添加public static ArrayList single(ArrayList al) { ArrayList t ...
  • 怎麽禁止浏覽器自動保存密碼?背景:有時不是記住密碼就方便,有時是記錄了反而不好,不安全呵.1)首先大部分浏覽器都是根據表單域的type="password"來判斷密碼域的,所以針對這種情況可以采取 ...
  •  首先可以確定的一點是, 不同類型節點的載入順序與它們在web.xml裏出現的次序無關,即不會因爲filter寫在listener之前而先載入fiter.     就<context-param>.< ...
  • 由于PHP的很多高階教材都是以LINUX作爲基礎,我也只好硬著頭皮從WINDOWS轉向LINUX,說實在的,這是個很痛苦到過程,從一個傻瓜 式到桌面系統轉到一個繁瑣到命令式系統,就像從移動電話時代回到了電報時代.又是 ...
  • // // Math.h // fastdelegate // // Created by Kink on -18. // Copyright (c) 2014年 Kink. All rights reser ...
  • MySQL添加字段應該如何實現呢?這是很多剛剛接觸MySQL數據庫的新人都提到過的問題,下面就爲您介紹MySQL添加字段和刪除字段的方法,希望對您能有所啓迪.MySQL添加字段:alter table `user_m ...
  • 今天看了些裝飾器的問題:在一篇博客http://www.cnblogs.com/huxi/archive/2011/03/01/1967600.html裏講到: total_ordering(cls):     這個 ...
  • 操作系統:windows xp運行軟件:WMware異常:Unable to open kernel device "\\.\VMCIDev\VMX": 重疊 I/O 操作在進行中. Did yo ...
  • 回車.換行.空格的ASCII碼值回車,ASCII碼13換行,ASCII碼10空格,ASCII碼32Return   =   CR   =   13   =   \x0dNewLine   =   LF   =   1 ...