[shell问答录]:命令、进程、子shell...
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                [shell问答录]:命令、进程、子shell...
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.                        
                                2019獨角獸企業(yè)重金招聘Python工程師標準>>>
| 前些天在CU上討論一個統(tǒng)計正在執(zhí)行的腳本數(shù)量的問題過程中,發(fā)現(xiàn)自己對于shell如何執(zhí)行命令方面了解還是甚少,慚愧慚愧...期間得到waker兄的指點,在此表示感謝!他的說法除了個別地方不太準確外,基本上是正確的。這些天抽時間找了些資料研究了一下,又學到了不少!這里把我的一點心得以問答的形式貼出來,供大家參考。小弟才疏學淺,錯誤的地方一定很多,歡迎大家拍磚、指正! Q1: shell如何執(zhí)行“簡單”命令? A: 這里的簡單命令和bash參考手冊里的含義相同,形式上一般是:命令的名稱加上它的參數(shù)。有三種不同的簡單命令: 1.內(nèi)置命令(builtin) 是shell解釋程序內(nèi)建的,有shell直接執(zhí)行,不需要派生新的進程。有一些內(nèi)部命令可以用來改變當前的shell環(huán)境,如: cd /path var=value read var export var ... 2.外部命令("external command" or "disk command") 二進制可執(zhí)行文件,需要由磁盤裝入內(nèi)存執(zhí)行。會派生新的進程,shell解釋程序會調(diào)用fork自身的一個拷貝,然后用exec系列函數(shù)來執(zhí)行外部命令,然后外部命令就取代了先前fork的子shell。 3.shell腳本(script) shell解釋程序會fork+exec執(zhí)行這個腳本命令,在exec調(diào)用中內(nèi)核會檢查腳本的第一行(如:#!/bin/sh),找到用來執(zhí)行腳本的解釋程序,然后裝入這個解釋程序,由它解釋執(zhí)行腳本程序。解釋程序可能有很多種,各種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環(huán)境中執(zhí)行的,不會影響當前shell的環(huán)境。 Q2: shell腳本是否作為單獨的一個進程執(zhí)行? A: 不是,shell腳本本身不能作為一個進程。如上面講的,shell腳本由一個shell解釋程序來解釋、運行其中的命令。這個shell解釋程序是單獨的一個進程,腳本中的外部命令也都作為獨立進程依次被運行。這也就是為什么ps不能找到正在運行的腳本的名字的原因了。作為一個替代方案,你可以這樣調(diào)用腳本: sh script-name 這時shell解釋程序“sh”作為一個外部命令被顯式地調(diào)用,而script-name作為該命令的命令行參數(shù)可以被我們ps到。 另外,如果你的系統(tǒng)上有pidof命令可用,它倒是可以找出shell腳本進程(實際上應該是執(zhí)行shell腳本的子shell進程)的進程ID: pidof -x script-name Q3: shell何時在子shell中執(zhí)行命令? A: 在此我們主要討論Bourne shell及其兼容shell。在許多情況下shell會在子shell中執(zhí)行命令: 1.(...)結(jié)構(gòu) 小括號內(nèi)的命令會在一個子shell環(huán)境中執(zhí)行,命令執(zhí)行的結(jié)果不會影響當前的shell環(huán)境。需要注意是此時變量$$會顯示當前shell的進程id,而不是子shell的進程id。 參考: {...;}結(jié)構(gòu)中的命令在當前shell中執(zhí)行,(內(nèi)部)命令執(zhí)行的結(jié)果會影響當前的shell環(huán)境。 2.后臺執(zhí)行或異步執(zhí)行 command& 命令由一個子shell在后臺執(zhí)行,當前shell立即取得控制等候用戶輸入。后臺命令和當前shell的執(zhí)行是并行的,但沒有互相的依賴、等待關系,所以是異步的并行。 3.命令替換 `command`(Bourn shell及兼容shell/csh) $(command)(在ksh/bash/zsh中可用) 將command命令執(zhí)行的標準輸出代換到當前的命令行。command在子shell環(huán)境中執(zhí)行,結(jié)果不會影響當前的shell環(huán)境。 4.管道(不同的shell處理不同) cmd1|cmd2 cmd1和cmd2并行執(zhí)行,并且相互有依賴關系,cmd2的標準輸入來自cmd1的標準輸出,二者是“同步”的。 對管道的處理不同的shell實現(xiàn)的方式不同。 在linux環(huán)境下大多數(shù)shell(bash/pdksh/ash/dash等,除了zshell例外)都將管道中所有的命令在子shell環(huán)境中執(zhí)行,命令執(zhí)行的結(jié)果不會影響當前的shell環(huán)境。 Korn shell的較新的版本(ksh93以后)比較特殊,管道最后一級的命令是在當前shell執(zhí)行的。這是一個feature而非BUG,在POSIX標準中也是允許的。這樣就使下面的命令結(jié)構(gòu)成為可能: command|read var 由于read var(read是一個內(nèi)部命令)在當前shell中執(zhí)行,var的值在當前shell就是可用的。 反之bash/pdksh/ash/dash中read var在子shell環(huán)境中執(zhí)行,var讀到的值無法傳遞到當前shell,所以變量var無法取得期望的值。類似這樣的問題在各種論壇和news group中經(jīng)常被問到。個人認為command|read var的結(jié)構(gòu)很清晰,并且合乎邏輯,所以我認為Korn shell的這個feature很不錯。可惜不是所有的shell都是這樣實現(xiàn)的。:(如開源的pdksh就是在子shell執(zhí)行管道的每一級命令。 Korn shell對管道的處理還有一個特殊的地方,就是管道如果在后臺執(zhí)行的話,管道前面的命令會由最后一級的命令派生,而不是由當前shell派生出來。據(jù)說Bourne shell也有這個特點(標準的Bourne shell沒有測試環(huán)境,感興趣的朋友有條件的可以自行驗證)。但是他們的開源模仿者,pdksh和ash卻不是這樣處理。 最特殊的是zshell,比較新的zshell實現(xiàn)(好像至少3.0.5以上)會在當前shell中執(zhí)行管道中的每一級命令,不僅僅是最后一條。每一條命令都由當前shell派生,在后臺執(zhí)行時也是一樣。可見在子sehll中執(zhí)行管道命令并不是不得已的做法,大概只是因為實現(xiàn)上比較方便或者這樣的處理已經(jīng)成為unix的傳統(tǒng)之一了吧。;-) 讓我們總結(jié)一下,不同的shell對管道命令的處理可能不同。有的shell中command|read var這樣的結(jié)構(gòu)是ok的,但我們的代碼出于兼容性的緣故不能依賴這一點,最好能避免類似的代碼。 5.進程替換(僅bash/zsh中,非POSIX兼容) <(...) >(...) 與管道有點類似,例子:cmd1 <(cmd2) >(cmd3), cmd1, cmd2, cmd3的執(zhí)行是同步并行的。 <(command)形式可以用在任何命令行中需要填寫輸入文件名的地方,command的標準輸出會被該命令當作一個輸入文件讀入。 >(command)形式可以用在任何命令行中需要填寫輸出文件的地方,該命令的輸出會被command作為標準輸入讀入。 兩種形式中的command都在子shell環(huán)境中執(zhí)行,結(jié)果不會影響當前的shell環(huán)境。 6.if或while命令塊的輸入輸出重定向 在SVR4.2的Bourne shell中對此情況會fork一個子shell執(zhí)行if塊和while塊中的命令;在linux下似乎其它的shell中都不這樣處理。 7.協(xié)進程(ksh) 只有Korn shell和pdksh有協(xié)進程的機制(其它shell中可以用命名管道來模擬)。類似于普通的后臺命令,協(xié)進程在后臺同步運行,所以必須在子shell中運行。協(xié)進程與后臺命令不同的是它要和前臺進程(使用read -p和print -p)進行交互,而后者一般只是簡單地異步運行。 Q4: 既然在當前shell中執(zhí)行命令也會派生子shell,那么它與在子shell中執(zhí)行命令又有什么區(qū)別呢? A: 這種說法不準確。 在當前shell中執(zhí)行內(nèi)部命令不會派生子shell,因此有些內(nèi)部命令才能夠改變當前的shell執(zhí)行環(huán)境。 在當前shell中執(zhí)行外部命令或腳本時會派生子shell,所以這時命令的執(zhí)行不會影響當前 的shell環(huán)境。注意:子shell中執(zhí)行的內(nèi)部命令只會改變子shell的執(zhí)行環(huán)境,而不會改變當前shell(父shell)的環(huán)境。 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命令執(zhí)行腳本。 即在當前shell環(huán)境下執(zhí)行腳本,沒有子shell,也就沒有了子shell的煩惱。:) | 
轉(zhuǎn)載于:https://my.oschina.net/shangjx13/blog/33217
總結(jié)
以上是生活随笔為你收集整理的[shell问答录]:命令、进程、子shell...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Java描述设计模式(05):原型模式
- 下一篇: Maven 核心原理
