シェルがバックグラウンドとフォアグラウンドで両方の組み込みコマンドの標準入力をどのようにリダイレクトするかを明確にします。

シェルがバックグラウンドとフォアグラウンドで両方の組み込みコマンドの標準入力をどのようにリダイレクトするかを明確にします。

非常に小さな質問があります。このコマンドを例に挙げますcat < file.txt

シェルが<を表示すると、新しいプロセスを分岐してstdin(0ファイル記述子)をリダイレクトし、stdinをその特定のプロセス環境の対応するファイルにのみ変更します(組み込まれていないため)。

ただし、組み込みコマンドがある場合、シェルは新しいプロセスをフォークせずに、端末環境全体で指定されたファイルにstdinを変更します(完全な端末環境で動作しているかどうかはわかりませんが、シェルが実行するため)。新しいプロセスをフォークせずにプログラム(組み込み)に対してのみ行う方法がわからず、プログラムが完了したら/dev/pts/0(通常のstdin)に戻ります。

しかし、それは私にあいまいです。 2つの組み込みコマンド(そのうちの1つはバックグラウンドにあります)を実行し、両方ともstdoutまたはstdinを2つの異なるファイルにリダイレクトする場合、どちらもstdin(またはstdout)を独自のファイルに変更することを意味しますが、これは次のいずれか意味します。プログラムはフォークしないため、両方の標準入力を同時にリダイレクトできないため、他のプログラムの標準入力を使用します。

これは単なるケースです。上記が真であれば、stdinはフォークされていないため、組み込みプログラムのプログラムだけでなく、端末環境全体に対しても変更されます。

明確でなければ説明するのは本当に難しいですが、別の方法で説明します。組み込み機能を使用すると、シェルは新しいプロセスをフォークしません。 1つはbgで実行され、もう1つはfgで実行されます。どちらもstdinをリダイレクトするかstdinを別のファイルに変換する場合、シェル組み込みプログラムはプログラムの実行が終了するまで端末全体でstdinを変更するので、どのように同時に異なるstdinを持つことができますか?

答え1

シェルするさまざまな条件で組み込みコマンドをフォークします。条件の1つはバックグラウンドジョブを実行することです。ここprintで、とはzselect すべてバックグラウンド関数に組み込まれた関数ですinbg

#!/usr/bin/env zsh
zmodload zsh/system
zmodload zsh/zselect
function inbg { print BG PID $sysparams[pid] > pid.bg ; zselect -t 333 }
function infg { print FG PID $sysparams[pid] > pid.fg }
inbg &
infg
wait

実行されると、inbg関数は別のプロセスIDで実行されます。これは、プロセスツリーを確認するか、サブプロセスIDの正しいロギング($sysparams[pid]ZSHを介して、ここではシェルが異なる)で観察できます。

% zsh builtins & sleep 1; pgrep -lf builtins; wait
[1] 97170
97170 zsh builtins
97172 zsh builtins
[1]  + done       zsh builtins
% cat pid*
BG PID 97172
FG PID 97170

pid.bgこれにより、標準出力を pid.fgあるプロセスから別のプロセスに簡単に再接続できます。

それ以外の場合、「全端末環境」は何ら影響を受けません。シェル(またはUNIXのすべてのプロセス)はファイル記述子を再接続して、標準出力の出力をあるターゲットから別のターゲットに一時的に変更できます。これにより、組み込み関数はしばらく別の場所に基準を送信できます。

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    int nullfd, savefd;

    puts("out1");
    savefd = dup(STDOUT_FILENO);

    nullfd = open("/dev/null", O_WRONLY);
    close(STDOUT_FILENO);
    dup2(nullfd, STDOUT_FILENO);
    puts("nothing");
    close(nullfd);

    close(STDOUT_FILENO);
    dup2(savefd, STDOUT_FILENO);
    puts("out2");

    return 0;
}

上記の再配線を使用すると、nothingstdoutで印刷する/dev/nullputs(1つ以上のシステムコールが失敗しない限り、上記のコードは明確さをチェックしません):

% make redirect-stdout
cc     redirect-stdout.c   -o redirect-stdout
% ./redirect-stdout
out1
out2

この種の「再配線」の詳細については、APUE(Advanced Programming in Unix Env​​ironments)を参照pipe(2)してくださいdup(2)

プロセス状態

2つのスレッドを作成し、標準出力を2つの異なるファイルにリダイレクトすることで、標準I / Oのリダイレクトがプロセス固有であることを証明できます。

#include <err.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

pthread_cond_t th_cnd  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t th_mtx = PTHREAD_MUTEX_INITIALIZER;
void *threader(void *);

int main(void) {
    pthread_t xxx, yyy;
    pthread_create(&xxx, NULL, threader, (void *) "thread.x");
    pthread_create(&yyy, NULL, threader, (void *) "thread.y");
    pthread_cond_wait(&th_cnd, &th_mtx);
    return 0;
}

void *threader(void *ptr) {
    char *label = (char *) ptr;
    int fd, i;
    fd = open(label, O_APPEND | O_CREAT | O_WRONLY, 0666);
    if (fd < 0) err(1, "open failed");
    close(STDOUT_FILENO);
    dup2(fd, STDOUT_FILENO);
    for (i = 0; i < 5; i++) { puts(label); usleep(100000); }
    pthread_cond_broadcast(&th_cnd);
    return (void *) 0;
}

このコードで発生する可能性のある問題

% CFLAGS=-lpthread make thread-io-redirect
cc -lpthread    thread-io-redirect.c   -o thread-io-redirect
% rm thread.*
zsh: no matches found: thread.*
% ./thread-io-redirect
% stat -f '%N %z' thread.*
thread.x 0
thread.y 90

すべての出力が入力されます(またはシステム使用量に応じthread.yてすべて出力されるか、他の極端なケースが発生する可能性があります)。thread.xこれは、標準出力リダイレクトがプロセス全体に適用されることを示します。 (簡単な主張は、STDOUT_FILENOプロセスごとに1つの記述子のみがあり、同様の関数は対応するputs(3) 数値を1つだけ使用することです。)

関連情報