MSYS2での入力の終わり

MSYS2の各環境が何を対象としたプログラムを作成するのか理解してしまえば話は簡単。

foo.c
#include <stdio.h>
int main(void)
{
    int c;
    while ((c = getchar()) != EOF) printf("%c", c);
    fprintf(stderr, "EOF\n");
    return 0;
}

mingw64.exeまたはmingw32.exeにて、

$ gcc foo.c -std=c99 -pedantic -Wall -Wextra -o foo-mingw.exe

前述の通り、foo-mingw.exeはコマンドプロンプト用なので、コマンドプロンプトにて、

C:\> foo-mingw.exe
hello, world
hello, world
^Z
EOF

C:\> 

のように、標準入力の終了を知らせるEOFはCtrl+Zである。
このプログラムをMSYS2のデフォルトシェルであるbash上で実行しようとすると、

$ ./foo-mingw
hello, world

改行を入力しても標準出力への出力も無く、
この状態で入力を終了しようと、Ctrl+Dと打っても、全く終わらない。
それどころか、Ctrl+Cで強制終了させると、
その前に入力したCtrl+Dのせいなのか、シェルに返った途端にシェル自体が終了した。
foo-mingw.exeはコマンドプロンプト用ということなので、
Ctrl+Zで終了させようとすると、

$ ./foo-mingw
hello, world
Stopped
$ 

一見すると終了したように見えるが、結局標準出力には何も出力がないし、
EOF検出後に出るはずのEOF表示もない。
これは、Ctrl+Zが中断シグナルとして解釈されてシェルに戻ってしまったからである。
結局、foo-mingw.exeはコマンドプロンプト用であり、MSYS2環境用ではないということだ。

シンプルな解決策は、msys2.exeが提供する環境でプログラムを作成すればよい。
つまり、msys2.exe上で、

$ gcc foo.c -std=c99 -pedantic -Wall -Wextra -o foo-msys.exe

として作成したプログラムはMSYS2環境下で動作する。

$ ./foo-msys
hello, world
hello, world
EOF
$

この場合のEOFはもちろんCtrl+Dである。

もう一つの解決策はwinptyを使うことである。

$ pacman -Si winpty
リポジトリ             : msys
名前                   : winpty
バージョン             : 0.4.3-1
説明                   : A Windows software package providing an interface
                         similar to a Unix pty-master for communicating with
                         Windows console programs
...snip

これを使用すれば、mingw64.exeやmingw32.exeで作成したプログラムをMSYS2環境で使える。

$ winpty ./foo-mingw
hello, world
hello, world
^Z
EOF
$ 

この場合はEOFとしてコマンドプロンプト上と同様にCtrl+Zを使用することになる。
winptyの問題点は、標準入出力が端末に接続されていない場合には使えないことである。

$ winpty ./foo-mingw > hoge
stdout is not a tty
$ winpty ./foo-mingw < hoge
stdin is not a tty
$ winpty ./foo-mingw <<EOD
> hello, world
> EOD
stdin is not a tty
$ 

のように怒られてしまう。残念。