最近のほとんどのシェルでは、上矢印と下矢印を押すと、以前に実行したコマンドがプロンプトに表示されます。私の質問は、これはどのように機能するのですか?!
シェルがstdoutを何らかの形で操作して、すでに書き込まれたものを上書きしているように思えますか?
wget のようなプログラムもこれを行うことに気付きました。誰かがそれをどのように行うか知っていますか?
stdout を操作するのではなく、端末によって既に表示されている文字を上書きしています。
これを試して:
#include <stdio.h>
#include <unistd.h>
static char bar[] = "======================================="
"======================================>";
int main() {
int i;
for (i = 77; i >= 0; i--) {
printf("[%s]\r", &bar[i]);
fflush(stdout);
sleep(1);
}
printf("\n");
return 0;
}
の出力にかなり近いwget
ですね。 \r
端末が「カーソルを現在の行の先頭に戻す」と解釈するキャリッジリターンです。
シェルが の場合、 GNU Readline ライブラリbash
を使用します。これは、端末タイプの検出、履歴管理、プログラム可能なキー バインドなど、より一般的な機能を提供します。
もう1つ-疑わしい場合は、wget、シェルなどのソースがすべて利用可能です。
現在の標準出力行 (またはその一部) を上書きするには、\r
(または\b
.) を使用します。特殊文字\r
(キャリッジ リターン) はキャレットを行の先頭に戻し、上書きできるようにします。特殊文字\b
は、キャレットを 1 位置だけ戻して、最後の文字を上書きできるようにします。
#include <stdio.h>
#include <unistd.h>
int i;
const char progress[] = "|/-\\";
for (i = 0; i < 100; i += 10) {
printf("Processing: %3d%%\r",i); /* \r returns the caret to the line start */
fflush(stdout);
sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);
printf("Processing: ");
for (i = 0; i < 100; i += 10) {
printf("%c\b", progress[(i/10)%sizeof(progress)]); /* \b goes one back */
fflush(stdout);
sleep(1);
}
printf("\n"); /* goes to the next line */
fflush(stdout);
標準出力は通常バッファリングされ、情報が出力または端末にすぐに出力されない可能性があるfflush(stdout);
ため使用します。
\r と \b に加えて、コンソール画面の内容を高度に制御するncursesを調べてください。(列を含む、任意に移動するなど)。
それはreadlineライブラリで行われます...舞台裏でどのように機能するかはわかりませんが、stdout やストリームとは何の関係もないと思います。readline はある種の暗号化された (少なくとも私にとっては) 端末コマンドを使用していると思われます。つまり、実際にシェル セッションを表示する端末プログラムと連携します。出力を印刷するだけで readline のような動作が得られるかどうかはわかりません。
(これについて考えてみてください: stdout はファイルにリダイレクトできますが、上/下矢印キーのトリックはファイルでは機能しません。)
プログラムは、端末が特別な方法で解釈する特殊文字を出力することによってこれを行います。これの最も単純なバージョンは (ほとんどの linux/unix 端末で) '\r' (キャリッジ リターン) を通常の stdout に出力し、カーソル位置を現在の行の最初の文字にリセットすることです。したがって、次に書くことは、前に書いた行を上書きします。これは、たとえば、単純な進行状況インジケーターに使用できます。
int i = 0;
while (something) {
i++;
printf("\rprocessing line %i...", i);
...
}
しかし、さまざまな方法で解釈される、より複雑なエスケープ文字シーケンスがあります。画面上の特定の位置にカーソルを配置したり、テキストの色を設定したりするなど、あらゆる種類のことがこれで実行できます。これらの文字シーケンスが解釈されるかどうか、またはどのように解釈されるかは端末によって異なりますが、ほとんどの端末でサポートされている一般的なクラスはansi エスケープ シーケンスです。したがって、赤いテキストが必要な場合は、次を試してください。
printf("Text in \033[1;31mred\033[0m\n");
これをシミュレートするには、キャリッジ リターンを使用します。
#include <stdio.h>
int main(int argc, char* argv[])
{
while(1)
{
printf("***********");
fflush(stdout);
sleep(1);
printf("\r");
printf("...........");
sleep(1);
}
return 0;
}
最も簡単な方法は、キャリッジ リターン文字 ('\r') を stdout に出力することです。
カーソルが行頭に移動し、内容を上書きできるようになります。