サブシェルをサイレントに強制終了しますか?

サブシェルをサイレントに強制終了しますか?

このようなものを実装したい質疑応答ただし、サブシェル用です。以下は私が試しているものの最小限の例です。

(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実行できる。sleepexec
  • SIGTERMの代わりにSIGPIPEを使用すると、メッセージを回避することができます。
  • リストコンテキストで変数を引用符で囲まないままにしておくと、 では非常に特別な意味を持ちますbash

以上のことを踏まえて、次の操作を実行できます。

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

(を強制終了してサブシェルだけを強制終了したくない場合はsleep 60に置き換えます。この場合、強制終了するまでにサブシェルが実行される時間すらない可能性があります)。exec sleep 60killsleepsleep

いずれにせよ、それで何を達成したいのかわかりません。

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. サブシェル
  2. 1 の子として sudo
  3. 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同じプロセスで実行されることになります。$BASHPIDexec

パイプを通じて pid を取得し、killroot として実行します。

もう少し待つ覚悟があれば、

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を待機しません。sudogrep

詳細は一致が見つかった後、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コマンド出力をリダイレクトすると、これが実行されます。

関連情報