web-dev-qa-db-ja.com

サブシェルを黙って殺しますか?

このようなものを実装したい Q/A ただし、サブシェル用です。これが私が試していることの最小限の例です:

(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
4

注意:

  • _wait $subshell_はwaitを実行しているプロセスの子ではないため、_$subshell_は機能しません。とにかく、waitを実行するプロセスを待つ必要はないので、それほど重要ではありません。
  • _kill $subshell_はサブシェルを強制終了しますが、sleepが実行されるまでにサブシェルがサブシェルを開始できた場合は、killを強制終了しません。ただし、sleepと同じプロセスでexecを実行できます。
  • sIGTERMの代わりにSIGPIPEを使用してメッセージを回避できます
  • リストコンテキストで変数を引用符で囲まないでおくことは、bashで非常に特別な意味を持ちます。

以上のことをすべて言ったので、次のことができます。

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

killsleepを殺すには、_sleep 60_を_exec sleep 60_に置き換えます。サブシェルだけではなく、sleepを殺します。

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

_sleep 600 &
_

sleepをバックグラウンドで開始する場合は、より信頼性の高い方法です(または、そのsleepプロセスをメインシェルから非表示にする場合は_(sleep 600 &)_)。

今あなたの実際の

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

コマンドでは、Sudoがコマンドを実行するための子プロセスを生成することに注意してください(ステータスをログに記録するか、後でいくつかのPAMセッションタスクを実行する必要がある場合のみ)。ただし、stdbufは同じプロセスで_wpa_supplicant_を実行するため、最終的には_wpa_supplicant_の祖先に(スクリプトの残りの部分に加えて)3つのプロセスがあります。

  1. サブシェル
  2. 1の子としての須藤
  3. 2の子としてのwpa_supplicant(以前はstdbufを実行していた)

1を殺しても、自動的に2は殺されません。ただし、2を殺した場合、傍受できないSIGKILLのようなシグナルがない限り、Sudoが受信したシグナルをコマンドに転送するため、3が殺されます。実行します。

いずれにせよ、それはあなたがここで殺したいサブシェルではありません、それは3または少なくとも2です。

これで、rootとして実行されていて、スクリプトの残りの部分が実行されていない場合、スクリプトを簡単に強制終了することはできません。

killrootとして実行する必要があるため、次のものが必要です。

_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_は、execで作成しているのと同じ_$BASHPID_プロセスでサブシェルとして実行されます。

パイプを通じてpidを取得し、killをルートとして実行します。

もう少し待つ準備ができている場合は、

_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はなくなりました。

一部のシェル実装は、Sudoが戻った後(SIGPIPEdを取得するまでバックグラウンドで実行したまま)grepを待機しません。また、bashを使用すると、grep ... <(Sudo ...)構文を使用して待機することもできます。bashは待機しません。 Sudoが戻った後のgrepの場合。

詳細 一致を見つけた後、Grepが終了するのが遅い?

7

サブシェルとは、bash -iプロンプトを提供する$インタラクティブログインシェルの子など、一部のシェルの子であるシェルコマンドを指します。サブシェルでコマンドを実行するhaveする必要はありません-独立したプロセスとして実行することを選択できます。 stdout/stderrがプログレスバーの外観を台無しにしたくないため、また親シェルがその子の死について報告したり、気づいたりすることを望まないため、これは適切であるように思われます。

daemonize やNohupなど、これを実現するための標準ツールがあります。 ( manpages も参照してください。) Nohup を使用するのが最適な場合があります。これを使用して、notnohup.outを作成する簡単なプログラムを実行する例を次に示します。

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

プログラムまたはプログラムのラッパースクリプトで、そのPIDを/tmp/my.pidに記録します。bashはそれを$$変数として使用できるようにします。次に、プログレスバーを使用した監視プロセスで

$ kill `cat /tmp/my.pid`

そのプログラムがそれ以上処理を行う必要がなくなったとき。または、プログラム名をkillallに指定することもできます。

2
J_H

あなたはこれを探しているかもしれません

#!/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

この例は、subshell doneを出力する前に1秒間停止します。この例では、背景をすべて同じラインで待機する方法も示しています。 & wait 2>wait_outputwait $!の例に比べて長所/短所があるかどうかはわかりません。

ここで注意すべき重要な点は、Terminatedメッセージが最上位の親シェルジョブコントロールから送信されることです。これが、サブシェルが終了し、メッセージを生成するのを確認するものです。したがって、ここで出力をキャッチします。 waitコマンド出力をリダイレクトすると、これが行われます。

1
Chris Hill