リダイレクトの挙動

  • 17
    Like
  • 0
    Comment
More than 1 year has passed since last update.

シェルでの実装場所が分からなかったので、straceを使ってシステムコールレベルでの挙動を確認してみました。
使用するシェルによって細かい部分は異なるが、大まかな流れということで。

標準出力リダイレクト

まずは、以下のようなechoで文字列をファイルに書き込む場合を確認する。

$ strace sh -c "echo hoge > sample.txt"
(省略)
open("sample.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_DUPFD, 10)                   = 10
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "hoge\n", 5)                   = 3
dup2(10, 1)                             = 1
close(10)                               = 0

上記処理を順に追っていくと、

1 sample.txtをopenし、3が割り当てられる。
2 fcntlにて10以降の空きのファイルディスクリプタ、今回は10に標準出力のファイルディスクリプタが複製される。

3 標準出力のファイルディスクリプタは10に複製されたのでcloseする。
4 sample.txtのファイルディスクリプタをdup2で1に複製することで、標準出力への書き込みがsample.txtに書き込まれる。
kobito.1400498746.667719.png

5 ファイルへの書き込みは1を通じて行われるので3はcloseする。
6 通常はwrite(1,...)とすることで、標準出力に結果が表示されるが、1はsample.txtとなっているため結果はファイルに書き込まれる。

kobito.1400498817.434999.png

7 標準出力のファイルディスクリプタを元に戻すためdup2で10を1に複製する
8 複製された標準出力のファイルディスクリプタは不要のため10をcloseする。

kobito.1400498906.886126.png

以上のように標準出力のファイルディスクリプタを退避させ、1に書き込み対象のファイルディスクリプタを割り当てることで標準出力をファイルへ書き込むことを実現しているようです。

ちなみに、>>でファイルに上書きする場合は、openフラグのO_TRUNCO_APPENDとなり、ファイル先頭から上書きでなく、末尾から追記となる。

標準出力と標準エラー出力を同時にリダイレクト

2>&1を使って標準出力と標準エラー出力の両方出力する場合も確認してみます。

$ strace -f sh -c "ls foo > err.txt 2>&1"
(省略)
open("err.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_DUPFD, 10)                   = 10
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
fcntl(2, F_DUPFD, 10)                   = 11
close(2)                                = 0
fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
dup2(1, 2)                              = 2
(省略)
[pid  5851] write(2, "ls: ", 4)         = 4
[pid  5851] write(2, "cannot access foo", 17) = 17
[pid  5851] write(2, ": No such file or directory", 27) = 27
[pid  5851] write(2, "\n", 1)           = 1
[pid  5851] close(1)                    = 0
[pid  5851] close(2)                    = 0
(省略)
dup2(10, 1)                             = 1
close(10)                               = 0
dup2(11, 2)                             = 2
close(11)                               = 0

上記の場合も標準出力1と標準エラー出力2はそれぞれ10と11に退避させ、1と2が書き込み対象ファイルに割り当てられます。

kobito.1400499756.569687.png