web-dev-qa-db-ja.com

ターミナルとシェルの相互作用を理解する

長い間、UNIXライクなシステムのターミナルについての私の理解は、それがシェルプロセスを起動し、と通信することによってそれにユーザーインターフェイスを提供するということでしたそれはstdinstdoutstderrです。

しかし、最近、cygwinターミナルを介してWindowsコンソールアプリケーションを起動する際の問題を調査しているときに、それほど単純ではない可能性があることに気付きました。

http://cygwin.com/1.5/cygwin-ug-net/using-effectively.html なるほど、

別の問題は、コンソールベースのWindowsプログラムからの出力の受信または入力の提供です。残念ながら、Windowsコンソールアプリケーションとの対話は、翻訳ユーティリティを使用するという単純な問題ではありません。 Windowsコンソールアプリケーションは、command.comまたはcmd.exeで実行するように設計されており、他の状況に適切に対処しないものもあります。 Cygwinは、コンソール(DOSボックス)でも実行されている場合にのみコンソール入力を受け取ることができます。これは、Windowsがコンソールデバイスのバックエンドに接続する方法を提供していないためです。もう1つの従来のUnix入出力メソッドであるptys(疑似端末)は、Cygwinでサポートされていますが、Windowsで完全にサポートされているわけではありません。基本的な問題は、Cygwin ptyがパイプであり、一部のWindowsアプリケーションが入力または出力をパイプにリダイレクトすることを好まないことです。

私はVC++のcl.exeを使用してWindowsでコンパイルした小さなCプログラムを作成しました-

#include <stdio.h>

int main(int argc, char *argv[]) {
    #define BUFFER_LEN 1024
    char buffer[BUFFER_LEN];

    printf("echo server started\n"); 
    while (fgets(buffer, BUFFER_LEN, stdin) != NULL) {
        printf("%s", buffer);
    }

    return 0;
}

Cygwinターミナル(mintty.exe)を実行してこのプログラムを起動すると、対話できません-

[puneet@freestyle ~]$ /cygdrive/c/echo1.exe
Hello

^-応答なし

しかし、私がそれをパイプに入れると、それは機能します-

[puneet@freestyle ~]$ echo  -e "1\n2\n3" | /cygdrive/c/echo1.exe | while read line; do echo $line; done
1
2
3
[puneet@freestyle ~]$

基本的に、mintty.exe端末との対話に失敗します。ただし、Windowsコンソールから直接bash.exeを実行すると、-と正しく対話できます。

[puneet@freestyle ~]$ /cygdrive/c/echo1.exe
Hello
Hello
^Z
[puneet@freestyle ~]$

次に、sshを自分のマシンに入れて、このプログラムをコマンドとして実行すると、端末が直接対話するのではなく、SSHサーバーと対話するように機能すると思いました。意志。しかし、それもうまくいきません-

[puneet@freestyle ~]$ ssh freestyle /cygdrive/c/echo1.exe
Hello

^-応答なし

しかし、これを再びパイプに入れることはうまくいきます! -

[puneet@freestyle ~]$ echo  -e "1\n2\n3" | ssh freestyle /cygdrive/c/echo1.exe | while read line; do echo $line; done
1
2
3
[puneet@freestyle ~]$

誰かがこれらすべての観察の背後にある理論を説明できますか?

ターミナルとシェル間の相互作用は、シェルのstdinstdout、およびを使用するだけではありません。 stderr

Windowsコンソールはどう違うのですか? cygwinプログラムとパイプを組んでいるときに、Windowsコンソールプログラムが正常に機能しているように見えるのはなぜですか?

3
0cd

fflush(stdout)whileループに(printfの後に)追加すると、mintty内でも、プログラムは期待どおりに機能します。また、stdoutで最初に実行する操作としてsetbuf(stdout, NULL)を呼び出して、それを機能させることができるはずです。

Windowsコンソールウィンドウ内でc:\ cygwin\bin\bashを実行することもでき、元のプログラムは例外として機能します。私は試していませんが、conemuまたはconsole2ウィンドウ内でbashを実行して、元のプログラムを期待どおりに動作させることもできるはずです。

つまり、これはミンティについてです。

何が起こっているのか(の一部)は次のとおりです。

Windowsコンソールは非常に特別です。 Windowsには、クライアント/サーバーランタイムサブシステムと呼ばれるサービスがあります。 Windowsでcmdプロンプトを起動すると、実際に実行しているのはクライアント/サーバーランタイムサブシステムに接続することであり、itがウィンドウを作成します。

一方、Minttyは「通常の」ウィンドウです(ターミナルエミュレーターを実行している場合があります)。

MicrosoftのCライブラリI/Oルーチンは、クライアント/サーバーランタイムサブシステムによって作成されたウィンドウで実行されているかどうかを実際にテストし、実行されている場合は動作を変更します。彼らが行う変更の1つは、stdoutの完全なバッファリングをオフにすることです。

Minttyで実行すると、ウィンドウのgets/putsルーチンは、コマンドプロンプトで実行されていないと見なすため、完全なバッファリングを実行します。

もう1つのトリックがあります。bashの下でminttyの下で実行するとcat | echo1.exeを実行できます。次に、いくつか入力すると、^Dを押すと、バッファリングされたstdout出力がすべて一度に表示されます。

cat | echo1.exeトリックが機能する理由は、catがcygwinプログラムであるためです。 Cygwinは、本質的に、posixエミュレーションライブラリです。したがって、stdincatはCygwinライブラリによってエミュレートされ、Cygwinライブラリは^Dを「実際の」Windows標準入力とは異なる方法で処理します。

しかし、bashの下でminttyの下でecho1.exeを実行すると、キーストロークはエミュレートされていないファイルストリームに入ります(つまり、stdinは実際のWindows stdinであり、エミュレートされたものではありません) Cygwinライブラリによる)、したがって、^Dはeofを送信しません。代わりに、^Zはeofを送信しますが、^Zはbashに特別な何かを意味するため、送信されません。むしろ、^Cを押す必要があります。これにより、echo1.exeがすぐに終了します(バッファはフラッシュされません。これは正しいです)。

3
Wandering Logic