Cで他のファイル記述子に書き込むことはできますか?

Cで他のファイル記述子に書き込むことはできますか?

すべての標準出力をファイルにリダイレクトするには、次のようにします。

my_prog 1> out

stderrで同じことを行うには、次のようにします。

my_prog 2> err

しかし、私はシェルに別のファイル記述子があることを知っています。たとえば、上位回答この問題3番目のファイル記述子を使用すると、コマンドラインを介してデータを送信できます。

echo "Some console message" 1>&3

Cプログラムがこのファイル記述子に書き込む方法はありますか?他のプログラムから読み取らずに書き込むだけで、出力は端末に送信されますか?

答え1

いくつかのことを明確にするために。存在する:

echo foo >&3

echo"foo\n"ファイル記述子3には書きません。echo常にファイル記述子1のstdoutに書き込みます。write()最初の引数として整数、2番目の引数で始まるメモリ領域へのポインタ、および1()の長さを使用してシステムコールを呼び出します。第三に。foo\n4foo\n

C言語ではこれを書くことができますwrite(1, "foo\n", 4)。上記のコードでは、シェルはfd 1を同じコードにリダイレクトします。ファイル説明を開くecho(システムコールを介して)呼び出す前にfd 3を開きますdup2()。したがって、機能的には同じですが、doと同じではありませんwrite(3, "foo\n", 4)。実際には(単純化)次のようになります。

if (pid = fork())
  waitpid(pid, ...);
else {
  dup2(3, 1);
  execlp("echo", "foo", 0);
} 

そしてecho作りました。write(1, "foo\n", 4)

ほとんどすべてのシェルを除外すると組み込まれているため、またはechoありません。代わりに、シェルは次のことを行います。forkexec

saved_stdout = dup(1);
dup2(3, 1);
builtin_echo("foo");
dup2(saved_stdout, 1); close(saved_stdout); /* restore stdout */

(同じプロセスでbuiltin_echo()これを行う関数はどこにありますか?)write(1, "foo\n", 4)

これを実行するコマンドについては、/の組み込みコマンドを見るwrite(3, "foo\n", 4)ことができます。kshzshprint -u3 foo

各プロセスにはファイル記述子があります。 0、1、2を除いて、規則に従ってstdin、stdout、およびstderr用に予約されています。他のfdは通常特別ではありませんが、シェル(アプリケーションの1つのタイプのみ)では、fd 0から<some-value>値が9以上の特定の場所まで(シェルの)ユーザーが使用するように予約されています。シェルはその中にスープを入れるために混ざりません。たとえば、saved_stdout = dup(1)上記の私は近似です。実際、シェルはsaved_stdout値が<some-value>

fd に 0、1、2 以外のルールが付いていないので、fd 3 では何も開くことができません。おそらく閉じているでしょう。そうでなければ、スクリプト呼び出し元はO_CLOEXCfd 3で何も開くことを期待していないため、スクリプトを開く理由がないため、スクリプトを閉じること(またはフラグを追加する)を忘れてしまった可能性があります。

fd 3に何かが開いていることがわかっている場合(通常は同じスクリプトでユーザーが事前に)、fd 3を使用できます。たとえば、次のようになります。

 {
   var=$(cmd 2>&1 >&3)
 } 3>&1

dup()ここで、fd 3はfd 1のaとして定義されます。今後これを使ってfd 1にcmd戻ります。dup()

答え2

ディスクリプタが開いていると仮定すると、Cはディスクリプタにアクセスできますwrite(または最終的に帰結するいくつかの上位レベルの呼び出しを使用する可能性が高い)。write(2)ここにあります。write2three.c

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    int fd;
    if ((fd = dup(3)) == -1) err(1, "dup failed");
    dprintf(fd, "hello\n");
    exit(EXIT_SUCCESS);
}

例えば

$ make write2three  
cc     write2three.c   -o write2three
$ ./write2three
write2three: dup failed: Bad file descriptor
$ rm out
$ ./write2three 3>out
$ cat out
hello
$ 

後でシェルでクリーンアップする必要があるかもしれません。

$ exec 3>&-
$ 

答え3

私はCに慣れていないので、より良い方法があるかもしれませんが、単にシステムコール(man 2 write)を使用するだけです。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

書き込みデータが転送される場所は、開いているファイル記述子によって異なります。ファイル記述子が端末に属する場合のみ、データが端末に到達します。

パイプまたはソケットに書き込んでもう一方の端がデータを読み取らない場合、write()バッファがいっぱいになると、書き込みアプリケーション(呼び出しなど)がブロックされます。

ファイル記述子を見ることができます。/proc/$PID/fd

答え4

Cで他のファイル記述子に書き込むことはできますか?

はい。

ほとんどのプロセスは、それを実行する親プロセスから3つのオープンファイル記述子を継承します。

stdinstdoutそしてstderr。これはそれぞれファイル記述子 0 - 2 です。

私はfd 0(stdin)が読み取り専用モードで開いていると思うので、Cプログラムはこのファイル記述子を読み取ることができますが、書き込むことはできません。

プログラムが別のファイルを開くと、増分順に次のファイル記述子を取得します。

関連情報