新井です。

$stdin、$stdout、$stderr のドキュメントを考えてました。

: $stdin
: $stdout
: $stderr
    標準入力,標準出力,標準エラー出力.

    これらへの代入はリダイレクトの意味になります。つまり、

        $stdout = File.open(file, 'w')

    を行うと、以後 $stdout への出力は file への出力になると同時に
    標準出力(STDOUT)への出力も file への出力になります。ただし、
    $stdout と STDOUT の fd の値は異なります。

        p obj = File.open('/tmp/file', 'w') # => #<File:0x401a6b44>
        p obj.fileno                        # => 4
        # これ以降の出力は実際には /tmp/file の内容です
        p $stdout = obj                     # => #<File:0x401a6b44>
        p $stdout.fileno                    # => 4
        p STDOUT.fileno                     # => 1

    以下のようなことをしても標準出力が元に戻るわけではありません

        $stdout = File.open('/tmp/file', 'w')
        p 'foo'                 # => foo (/tmp/file の内容)
        $stdout = STDOUT
        p $stdout.fileno        # => 4   (/tmp/file の内容)

    最初の代入の時点で STDOUT はリダイレクトされているので、
    これにさらにリダイレクトさせても無意味だからです。

    (((-あらい 2001-08-30: ruby version 1.7 では実験的に $stdout =
    STDOUT は元に戻るようになっていますが、何か無理矢理な感じがします。
    あるべき挙動はまだ未定義です。個人的には1.6 の挙動の方が常にリダイ
    レクトという点で一貫していて良いです…が、-)))

    本当に元に戻したい場合は、以下のようにします。

        sv = STDOUT.dup
        $stdout = File.open('/tmp/file', 'w')
        p 'foo'                 # => foo  (/tmp/file の内容)
        $stdout = sv
        p $stdout.fileno        # => 4    (画面出力)

    fileno まで戻したければ再度 (({$stdout = STDOUT})) を
    行います。((-のように、不便な面があります-))

以上、上で書いた通り、個人的には 1.7 のように変な小細工をす
るよりは 1.6 の仕様の方が良い感じです。で、上で問題にしてい
る元に戻す方法ですが、以下のようにするのはどうでしょう?

	sv = STDOUT.dup
        $stdout = File.open('/tmp/file', 'w')
        p $stdout.fileno        # => 1    (/tmp/file の内容)
        $stdout = sv
        p $stdout.fileno        # => 1    (画面出力)

つまり、$stdin、$stdout、$stderr の fd 値は常に固定にすると
いうことです。C で書けばこうでしょうか?

	int sv = dup(1);
	FILE *fp = fopen("/tmp/file", "w");
	dup2(fileno(fp), 1);
	puts('foo');
	dup2(sv, 1);
	puts('bar');

いかがでしょう?より単純でわかりやすいルールだと思います(な
んか穴がありそうだけど)

# どうでもいいけど、この説明は長すぎだあ。ボツだあ。

--
新井康司 (Koji Arai)