2017-06-15 72 views
7

碰到一個意外的bash/sh行爲,我想知道有人可以解釋它背後的基本原理,併爲下面的問題提供解決方案。當執行單個命令時,Bash'吞嚥'子shell子進程

在交互式bash shell會話,我執行:

$ bash -c 'sleep 10 && echo'

隨着ps在Linux上看起來是這樣的:

\_ -bash \_ bash -c sleep 10 && echo \_ sleep 10

進程樹是我所期望的:

  • 我互動的bash shell進程($
  • 一個孩子shell進程(bash -c ...
  • 睡眠的孩子處理

但是,如果我bash -c命令部分是命令,例如:

$ bash -c 'sleep 10'

然後,中間子外殼被吞噬,並且當兒童處理時,我的交互式終端會話「直接」執行睡眠。 進程樹是這個樣子:

\_ -bash \_ sleep 10

所以從進程樹的角度來看,這兩個產生相同的結果:

  • $ bash -c 'sleep 10'
  • $ sleep 10

這是怎麼回事在這?

現在我的問題:有沒有辦法強制中間殼,無論表達式的複雜度傳遞給bash -c ...

(我可以追加類似; echo;到我的實際命令和「作品」,但我寧願不有沒有更合適的方式來迫使中間過程產生了。?)

(編輯:錯字在ps輸出;去除sh標籤在評論建議;多一個錯字)

+0

爲什麼你會*不*希望這種優化時,它的可能嗎? –

+0

在用戶可以傳遞任意命令的環境中處理子進程時,主要確保一致的行爲。我不確定繞過這個優化是我的解決方案(我遇到的實際問題必須處理sudo的更改:https://stackoverflow.com/a/34376188)。但是這種行爲很有趣,我想了解更多。 – Marco

+0

偉大的問題。你的意思是說你在第一個ps樹的第2行用'\ _ bash -c sleep 10 && echo'代替'\ _ bash -c sleep 10 && sleep 10'嗎? – codeforester

回答

4

這事實上是在評論in the bash source描述太多的理由爲這樣的特徵:

/* If this is a simple command, tell execute_disk_command that it 
    might be able to get away without forking and simply exec. 
    This means things like (sleep 10) will only cause one fork. 
    If we're timing the command or inverting its return value, however, 
    we cannot do this optimization. */ 
if ((user_subshell || user_coproc) && (tcom->type == cm_simple || tcom->type == cm_subshell) && 
    ((tcom->flags & CMD_TIME_PIPELINE) == 0) && 
    ((tcom->flags & CMD_INVERT_RETURN) == 0)) 
    { 
    tcom->flags |= CMD_NO_FORK; 
    if (tcom->type == cm_simple) 
     tcom->value.Simple->flags |= CMD_NO_FORK; 
    } 

bash -c '...'的情況下,CMD_NO_FORK標誌由should_suppress_fork functionbuiltins/evalstring.c確定。

它是總是爲了您的利益,讓shell執行此操作。

  • 輸入是硬編碼字符串,而外殼是在該字符串的最後一個命令:它只有在發生。
  • 命令完成後沒有更多的命令,陷阱,掛鉤等要運行。
  • 退出狀態不需要反轉或以其他方式修改。
  • 不需要重定向。

這樣可以節省內存,使進程的啓動時間要稍微快一點(因爲它並不需要是fork ED),並確保發送到您的PID信號直接到你的過程運行,使得sh -c 'sleep 10'的父母可以準確確定哪個信號被殺死sleep,實際上它是否應該被信號殺死。

但是,如果由於某種原因,你想抑制它,但你必須懂得設下的陷阱 - 任何陷阱會做:

# run the noop command (:) at exit 
bash -c 'trap : EXIT; sleep 10' 
+0

我從源頭上看不出來,但在我看來,Bash只會在整個'-c'命令行中只有一個命令。所以像'bash -c':; sleep 10「'這樣的東西即使在開始睡眠之後仍然運行。 – ilkkachu

+0

@ilkkachu,我直觀的預期是,這可能會因版本而異 - 3.2將在2014年年底實現'should_suppress_fork',f/e之前。即使是今天的情況,我也不希望它在未來的版本中仍然如此(希望能夠解決優化失敗的機會) - 而陷阱將總是*必須保持shell運行。 –

+0

似乎在3.2和4.4中都有相似的行爲。 – ilkkachu