默默殺死子shell?

默默殺死子shell?

我想實現這樣的事情問答但對於子外殼來說。這是我正在嘗試的一個最小示例:

(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)

echo subshell done

我怎麼能讓它只subshell done返回而不是:

./test.sh: line 4:  5439 Terminated              ( subshell=$BASHPID; ( kill $subshell && wait $subshell 2> /dev/null ) & sleep 600 )
subshell done

編輯:我在這裡的術語可能是錯誤的,我所說的子外殼是指第一組括號內的過程。

更新:

我想發布實際程式中的片段以了解上下文,上面是一個簡化:

# If subshell below if killed or returns error connected variable won't be set
(if [ -n "$2" ];then

      # code to setup wpa configurations here

      # If wifi key is wrong kill subshell
      subshell=$BASHPID
      (sudo stdbuf -o0 wpa_supplicant -Dwext -i$wifi -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 \
        | grep -m 1 "pre-shared key may be incorrect" \
        && kill -s PIPE "$subshell") &

      # More code which does the setup necessary for wifi

) && connected=true

# later json will be returned based on if connected is set

答案1

筆記:

  • wait $subshell不會工作,因為$subshell它不是您正在運行的進程的子進程waitwait
  • kill $subshell將殺死子 shell,但如果子 shell 在運行sleep時已成功啟動它,則不會殺死該子 shell。但是,您可以在同一進程中kill執行sleepexec
  • 您可以使用 SIGPIPE 而不是 SIGTERM 來避免該訊息
  • 在列表上下文中不加引號的變數在bash.

說了這麼多,你可以這樣做:

(
  subshell=$BASHPID
  kill -s PIPE "$subshell" &
  sleep 600
)
echo subshell done

(如果您想要殺死而不僅僅是子shell,請替換sleep 60為,在這種情況下,當您殺死它時,子shell甚至可能沒有時間運行)。exec sleep 60killsleepsleep

無論如何,我不確定你想用它來實現什麼。

sleep 600 &

sleep如果這就是您想要做的(或者(sleep 600 &)如果您想sleep從主 shell 中隱藏該進程),那麼在背景啟動將是更可靠的方法

現在與您的實際情況

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf

命令,請注意,它sudo會產生一個子進程來運行該命令(如果只是因為它可能需要記錄其狀態或隨後執行一些 PAM 會話任務)。stdbuf然而,將在同一個進程中執行wpa_supplicant,因此最終您將在wpa_supplicant的祖先中擁有三個進程(除了腳本的其餘部分):

  1. 子外殼
  2. 1 歲時的 sudo
  3. wpa_supplicant(之前運行 stdbuf)作為 2 的孩子

如果你殺死 1,那不會自動殺死 2 sudo。到它運行的命令。

無論如何,這不是您想要在這裡殺死的子 shell,它是 3 個或至少 2 個。

現在,如果它正在運行,root而腳本的其餘部分沒有運行,那麼您將無法如此輕鬆地殺死它。

您需要將其kill完成root,因此您需要:

sudo WIFI="$wifi" bash -c '
  (echo "$BASHPID" &&
   exec stdbuf -o0 wpa_supplicant -Dwext -i"$WIFI" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1
  ) | {
    read pid &&
      grep -m1 "pre-shared key may be incorrect" &&
      kill -s PIPE "$pid"
  }'

這樣,wpa_supplicant將在與子 shell 相同的進程中運行,$BASHPID就像我們使用exec.

我們透過管道獲取 pid 並kill以 root 身份運行。

請注意,如果您準備再等一會兒,

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 |
  grep -m1 "pre-shared key may be incorrect"

wpa_supplicant用 SIGPIPE 自動殺死(由系統,所以沒有權限問題)下次grep它在消失後會向該管道寫入一些內容。

有些 shell 實作不會等待after返回(讓它在後台運行,直到收到 SIGPIPED),並且使用,您也可以使用語法來做到這一點,where does not wait sudofor grepone bashafter grep ... <(sudo ...)has returned 。bashsudogrep

更多內容請參見Grep 找到匹配後退出很慢?

答案2

子 shell 是指屬於某個 shell 子層級的 shell 指令,例如為bash -i您提供提示的互動式登入 shell的子級$。你不在子 shell 中執行命令 - 您可以選擇將其作為獨立進程運行。聽起來這可能是合適的,因為您不希望它的 stdout / stderr 弄亂進度條的外觀,並且因為您不希望父 shell 報告甚至注意到其子 shell 的死亡。

有一些標準工具可以實現這一點,例如守護程式和 nohup。 (也可以看看男人 頁面.) 你可能最好的是諾哈普。這是一個使用它來運行一個簡單程式的範例,它確實不是建立 nohup.out:

$ nohup true 2>&1 > /dev/null &

讓您的程式或程式的包裝腳本將其 PID 記錄在 /tmp/my.pid 中—bash 使其可作為變數使用$$。然後用進度條監控進程即可

$ kill `cat /tmp/my.pid`

當它不再需要該程式進行任何更多處理時。或者,您可能更願意將程式名稱指定為killall.

答案3

您可能正在尋找這個

#!/bin/bash
(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) &
wait $! 2>/dev/null

echo subshell done

這裡子 shell 被置於後台,然後父 shell 等待,但等待輸出傳送到 /dev/null。這捕獲了Terminated訊息。

請注意,如果您更改等待捕獲輸出到文件,例如,wait $! 2>wait_output您將看到

 ./foo.sh: line 5:  1939 Terminated              ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )

顯示Terminated來自父 shell。

進行一個小檢查,看看在終止之前是否有一些活動,它是否有效

#!/bin/bash
(subshell=$BASHPID
 (sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output

echo subshell done

此範例將在列印前暫停一秒鐘subshell done。此範例還展示瞭如何在同一行上進行後台處理和等待,例如& wait 2>wait_output。我不確定這與wait $!.

這裡要注意的關鍵是訊息Terminated來自頂級父 shell 作業控制。這就是子 shell 終止並產生訊息的原因。這就是您想要捕獲輸出的地方。重定向wait命令輸出可以實現此目的。

相關內容