我想實現這樣的事情問答但對於子外殼來說。這是我正在嘗試的一個最小示例:
(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
它不是您正在運行的進程的子進程wait
。wait
kill $subshell
將殺死子 shell,但如果子 shell 在運行sleep
時已成功啟動它,則不會殺死該子 shell。但是,您可以在同一進程中kill
執行sleep
exec
- 您可以使用 SIGPIPE 而不是 SIGTERM 來避免該訊息
- 在列表上下文中不加引號的變數在
bash
.
說了這麼多,你可以這樣做:
(
subshell=$BASHPID
kill -s PIPE "$subshell" &
sleep 600
)
echo subshell done
(如果您想要殺死而不僅僅是子shell,請替換sleep 60
為,在這種情況下,當您殺死它時,子shell甚至可能沒有時間運行)。exec sleep 60
kill
sleep
sleep
無論如何,我不確定你想用它來實現什麼。
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 歲時的 sudo
- 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 sudo
for grep
one bash
after grep ... <(sudo ...)
has returned 。bash
sudo
grep
更多內容請參見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
命令輸出可以實現此目的。