bashインタラクティブシェルがプロンプトに書き込み、デフォルトでインタラクティブ入力をstderrにエコーするのはなぜですか?

bashインタラクティブシェルがプロンプトに書き込み、デフォルトでインタラクティブ入力をstderrにエコーするのはなぜですか?

私はbashインタラクティブシェルがデフォルトでプロンプトに書き込んで、ファイル記述子2(stderr)に入力したものをエコーすることに気づきました。これはstraceでbashを実行することで確認できます。

その理由は何ですか?なぜこの内容を標準出力に書きませんか?

答え1

もともとUNIXバージョン5-7も同じことをしていました。 (UFD output = 2; http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh/main‌​.c)

time確かに便利な stderr で出力する組み込み関数です。ただし、timeこれはV5に組み込まれている機能の1つではありません。

大きな理由はないと思います。いいえ端末出力を stderr に書き込みます。シェルの出力は、明示的なエラーメッセージまたは対話型端末専用出力です。標準出力をリダイレクトして対話型出力をリダイレクトする必要はありません。

stderr は V5 ではなく V6 で導入されましたが、V5 ではsh必要に応じて古い FD 2 を閉じてから手動で stdout を FD 2 に送信できます。失敗した同様のコマンドを実行しようとしたdup()ときにエラーメッセージを印刷する必要性が見つかったようです。exec()foo > output

今、方法を学びます。ポケット歴史的なUNIXコードは次のとおりです。物理RAMが必ずしも多い必要はないので、意図的に短く保たれた。

prs()文字列を出力する関数があります。 FDパラメーターを使用しません。 FD 2にエラーメッセージを印刷し、他の文字列もFD 2に印刷できるため、無条件にFD 2にのみ印刷します。いくつかの命令でコンパイルされた短いコードなので、最小限のRAMを使用してください。

しばらく何が起こると、変化彼らは常に自分が改善するよりも多くを破る危険があります。

私を恥ずかしくさせるのは、Python開発者がこれを見つけてコピーした理由です。私の考えでは、やや曖昧な事実です。たぶんこれは私がまだ見つけていない別の理由を意味するかもしれません。

err(s)
char *s;
{

    prs(s);
    prs("\n");
    if(promp == 0) {
        seek(0, 0, 2);
        exit();
    }
}

prs(as)
char *as;
{
    register char *s;

    s = as;
    while(*s)
        putc(*s++);
}

putc(c)
{

    write(2, &c, 1);
}

答え2

シェルは-cスクリプトを解釈せず(おそらくinline)、stdinが端末の場合は対話型ですが、以下のPOSIXシェルを参照してください。

この場合、同じ端末にプロンプ​​ト(そしてechoシェルが独自の行エディタを持つように入力したもの)が表示されます。問題は、標準入力が読み取り+書き込みモードで開くことを保証されていないため、標準入力へのプロンプト出力が信頼できないことです。さらに、人々は対話型セッションの途中でこれを実行しようとする可能性があり、exec < other-fileそれによってプロンプトの表示も中断される可能性があります。

zshユーザー対話のためにどの端末(使用済み)を決定ttyname()し、読み取り+書き込み(10以上の専用のスタンドアロンfdで)のために再度開くのがより賢明でより良い方法です。オープンファイル記述子/dev/tty(ほとんどの場合、stdinが端末の場合はstdinにあるものと同じ端末を参照する制御端末)を使用することは意味がありますが、どのシェルもこれを実行しないようです。コンソールのリカバリシェルなど、場合によって発生する可能性のある制御端末)

しかし、POSIX 仕様では、シェルがインタラクティブで、stderr も上記の要件を満たしている場合にのみ端末と指定されています。POSIXでは、stderrにプロンプ​​トを作成する必要があります。

bashPOSIXと互換性があるように設計されているため、これらの要件に従う必要があります。

私はこれが歴史的な理由だと思います。 POSIX仕様は、これがどのように機能するかとそれ以前のBourneシェルにsh基づいています。 (Bourneシェルが最初にリリースされたときにUnix V7にはすでに存在していましたが)。ksh88ksh88ttyname()

rmターミナルアプリケーションへのユーザーインタラクション(例:mv/ / ...プロンプトfind -ok)は通常stdin + stderrで行われます。 stderrは書き込みモードで開き、端末セッションで端末を指します。 stdoutはコマンドの通常の出力に使用され、リダイレクトを使用して通常の出力を保存または後処理するためにユーザー対話メッセージとは別に維持することが重要です。開いている内部fdの代わりにstderr(既知の特定のfd)に配置すると、/dev/ttyこれらのメッセージを簡単に削除したり(コマンドの実行など)、2> /dev/nullコマンドを非対話的に使用したりできます。

しかし、シェルの場合には特に有用ではありません。実行すると、sh 2> /dev/nullシェルが非対話型になります(つまり、プロンプトは表示されませんが、他の多くの副作用が発生することを意味します)。つまり、無効なプロンプトを使用することができますが、を使用してすべてのコマンドをexec 2> /dev/null実行しない限り、すべてのコマンドエラーを削除する副作用がありますcmd 2> something。 PS1、PS2、PS3、PS4をクリアする方がはるかに良いようです。

これにより、ユーザー入力がある端末から別の端末に出力される可能性がありますが、なぜ誰かがこれを望む理由を理解できません。

考えられる理由の1つは、これがより完璧になるということです。

  • /dev/tty上記のように制御端子がない極端な場合には動作しないことがあり、
  • ttyname()/devsが正しく満たされていないか、使用されていないと機能しない可能性がありますchroot

いくつかの他のシェルでは、状況が悪いです。csh、、、、、、、はプロンプトとstdoutに入力した内容のエコーを表示します(ただし、stdoutがターミナルではなくラインエディタが何も出力しtcshない場合、プロンプトは表示されません(ターミナルデバイスで生成されたttyラインに注意してください)。 )。決定する))。rcesakangafishfishcshecho

答え3

以前の答えを読む方法は次のとおりです。 bashは以前のバージョン、主にBourneとKornシェルの動作を模倣するためです。 「いつもそうだった」誰もが現在の状態を受け入れ、誰も現在の状態に疑問を提起しませんでした。申し訳ありませんが、これは不便です。

IMO、本当の理由stdoutリダイレクトの影響を受けるコマンドラインツール(シェルスクリプトを含む)は通常* ixシェル内で動作するため、パイプはあるプロセスの出力を別のプロセスの入力に渡すために使用されます。この場合、プロンプトは表示されず、パイプラインデータの一部になり、これは望ましくありません。

関連情報