lessは私のスクリプトを停止します。なぜこれが起こり、それを防ぐ方法は何ですか?

lessは私のスクリプトを停止します。なぜこれが起こり、それを防ぐ方法は何ですか?

s現在のディレクトリには次のBashスクリプトがあります。

#!/bin/bash
pipe_test() {
    ( set -m; (
        $1
    ); set +m ) | 
    (
        $2
    )
}
pipe_test "$1" "$2"

たとえば、私が電話した場合

./s yes less

スクリプトが停止しました。 (lessIEとIEの代わりに試した他のポケットベルを使用してもmore同様のことが起こりますmostfg

set -mサブシェルのプロセスに別のプロセスグループIDが付与されるように、サブシェルにジョブ制御(アクティブ化)があることを望みます。

私のシステムに関する情報:

$ bashbug
...
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -g -O2 -fdebug-prefix-map=/build/bash-cP61jF/bash-5.0=. -fstack-protector-strong -Wformat -Werror=format->
uname output: Linux jarnos-OptiPlex-745 5.4.0-29-generic #33-Ubuntu SMP Wed Apr 29 14:32:27 UTC 2020 x86_64 x86_64 x86_64 GNU>
Machine Type: x86_64-pc-linux-gnu

Bash Version: 5.0
Patch Level: 16
Release Status: release
$ less --version
less version: 551

答え1

これが発生するのは、ジョブ制御(set -m)を有効にすると、プロセスのグループ化だけでなく、「フォアグラウンド」および「バックグラウンド」ジョブを処理するためのメカニズムも提供されるためです。この「メカニズム」は、ジョブ制御が有効になると、順番に実行されるすべてのコマンドがフォアグラウンドプロセスグループになることを意味します。

つまり、そのサブシェル(パイプラインの左側)がジョブ制御を有効にすると、実際にその時点まで端末を所有していたパイプライン全体から端末を盗み、プロセスは例に含まれますless。背景になり、端末が使用できなくなります。その後less 、端末に接続し続けるので停止します。

発行によってfgターミナルがパイプライン全体に返されるので、返却時にすべてがless終了します。各追加コマンドは、ジョブ制御サブシェルで実行されない限り、端末を再度盗みます。

この問題を解決する1つの方法は、単にバックグラウンドでジョブ制御サブシェルを実行することです。

( set -m; (
        $1
    ) & set +m ) | 
    (
        $2
    )

必要に応じて異なるプロセスグループで実行されているコマンドを使用し$1、バックグラウンドモードはターミナルを盗み出してターミナルをパイプに残すのを防ぎます$2

もちろん、これ$1を行うには、inコマンドが端末自体を読み取ってはいけません。それ以外の場合は、端末自体を読み取ろうとするとすぐに停止します。

さらに、上記と同様に追加したい他のジョブ制御サブシェルは、追加するまで同じ「バックグラウンド」処理が必要です。set +mそれ以外の場合は、各追加ジョブ制御サブシェルが再生成されます。端末。

つまり、プロセスの終了のみを目的としてプロセスのグループ化が必要な場合は、プロセスのグループ化を使用してそのpkillプロセスをターゲットにすることを検討できます。例えば、pkill -P信号は対応するプロセスに送信される。表示されたPIDです。これにより、お子様のPIDがわかると、お子様のすべてのお子様(お子様は除く)を対象に指定することができます。

答え2

これを削除するとset -m問題が解決します(とにかくどうすればよいですか?)。

カーネルは次の方法で3つのプロセスを停止しますSIGTTOU

  • スクリプトプロセス
  • サブシェル
  • less

しかしyes、。そのプロセスはおそらく別のプロセスグループに配置されますset -m。したがって、カーネルはそのパイプ内のすべてのプロセスにアクセスしようとしますが、1つを見逃します。ただし、これらの不在が「中止」メッセージの原因ではありません。

通常、SIGTTOU端末に書き込もうとするバックグラウンドプロセスが原因です。しかし、これが唯一の理由ではありません。

int SIGTTOU
SIGTTIN と似ていますが、バックグラウンドジョブのプロセスが端末に書き込んだとき、またはそのモードを設定しようとしたときに生成されます。繰り返しますが、基本的な作業はプロセスを停止することです。 TOSTOP 出力モードが設定されている場合、SIGTTOU は端末に書き込もうとした場合にのみ生成されます。出力モードを参照してください。

バラよりhttps://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html

前の最後のシステムコールは次のとおりです(通過less)。

ioctl(3, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig -icanon -echo ...}) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)

だから私の評価は、奇妙な理由(たとえばset -m)のためにパイプが背景に配置されていることです。次のようないくつかのシステムコールがあります。

ioctl(255, TIOCSPGRP, [23715]

さまざまなプロセスを介して。最後はサブシェルを通して

ioctl(2, TIOCSPGRP, [23718]) = 0

yes(他のメンバーなしで)独自のプロセスグループのリーダーにし、フォアグラウンドプロセスグループにします。

setpgid(23718, 23718 <unfinished ...>

関連情報