このようなものを実装したい質疑応答ただし、サブシェル用です。以下は私が試しているものの最小限の例です。
(subshell=$BASHPID
(kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)
echo subshell done
次のように、代わりに returnのみを実行するにはどうすればよいですか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
サブシェルを強制終了するが、sleep
サブシェルが実行時に起動していた場合は終了しない。ただし、同じプロセスで次のようにkill
実行できる。sleep
exec
- SIGTERMの代わりにSIGPIPEを使用すると、メッセージを回避することができます。
- リストコンテキストで変数を引用符で囲まないままにしておくと、 では非常に特別な意味を持ちます
bash
。
以上のことを踏まえて、次の操作を実行できます。
(
subshell=$BASHPID
kill -s PIPE "$subshell" &
sleep 600
)
echo subshell done
(を強制終了してサブシェルだけを強制終了したくない場合はsleep 60
に置き換えます。この場合、強制終了するまでにサブシェルが実行される時間すらない可能性があります)。exec sleep 60
kill
sleep
sleep
いずれにせよ、それで何を達成したいのかわかりません。
sleep 600 &
sleep
バックグラウンドで起動する方が、より信頼性の高い方法になります(または、そのプロセスをメインシェルから(sleep 600 &)
隠したい場合)。sleep
実際の
sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf
コマンドを実行する場合、 はsudo
コマンドを実行するために子プロセスを生成します (後でステータスをログに記録したり、PAM セッション タスクを実行したりする必要がある場合があるため)。 ただし、 は同じプロセスでstdbuf
実行されるwpa_supplicant
ため、最終的には の祖先に 3 つのプロセス (スクリプトの残りの部分に加えて) が存在することになりますwpa_supplicant
。
- サブシェル
- 1 の子として sudo
- wpa_supplicant(以前はstdbufを実行していた)を2の子として
1 を強制終了しても、2 が自動的に終了するわけではありません。ただし、2 を強制終了する場合、傍受できない SIGKILL などのシグナルを使用しない限り、sudo
受信したシグナルが実行中のコマンドに転送されるため、3 も強制終了されます。
いずれにせよ、ここで削除したいのはサブシェルではなく、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
同じプロセスで実行されることになります。$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"
SIGPIPEで自動的に終了するwpa_supplicant
(システムによるので権限の問題ではない)次回grep
消えた後にそのパイプに何かを書き込みます。
sudo
一部のシェル実装では、 が返された後を待機しませんgrep
(SIGPIPE を受信するまでバックグラウンドで実行されたままになります)。 ではbash
、 の構文を使用してこれを行うこともできますgrep ... <(sudo ...)
。 は、が返された後もbash
を待機しません。sudo
grep
答え2
bash -i
サブシェルとは、プロンプトを表示する対話型ログインシェルの子など、何らかのシェルの子であるシェルコマンドを指します$
。持っているサブシェルでコマンドを実行するには、独立したプロセスとして実行することを選択できます。stdout / stderr によって進捗バーの外観が台無しになることは望ましくないため、また親シェルが子の終了を報告したり通知したりすることも望ましくないため、これが適切であると思われます。
それを実現するための標準的なツールとしては、悪魔化するおよび 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
ここでサブシェルはバックグラウンドに置かれ、親シェルは待機しますが、待機出力は /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
親シェルからのものであることを示しています。
キル前に何らかのアクティビティがあった場合にそれが機能するかどうかを確認するための小さなチェックは
#!/bin/bash
(subshell=$BASHPID
(sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output
echo subshell done
この例では、 を印刷する前に 1 秒間一時停止しますsubshell done
。また、この例では、 のようにすべてを同じ行でバックグラウンドにして待機する方法も示しています& wait 2>wait_output
。 を使用した例と比べて、これが利点/欠点であるかどうかはわかりませんwait $!
。
ここで注目すべき重要な点は、Terminated
メッセージがトップレベルの親シェルのジョブ制御から来ることです。これがサブシェルの終了を検出し、メッセージを生成するものです。したがって、出力をキャッチする場所はここです。wait
コマンド出力をリダイレクトすると、これが実行されます。