ターミナルシーケンスで既存の行を上書きする方法

ターミナルシーケンスで既存の行を上書きする方法

したがって、wgetWeb ページを取得すると、ファイルがどれだけダウンロードされたかを示すステータス バーが表示されます。次のようになります。

25%[=============>______________________________________] 25,000 100.0K/s (アンダースコアはスペースです。連続したスペースを複数入れる方法がわかりませんでした)

ただし、stdout に別の行を書き込んで別の進行状況バーを追加する代わりに、次のように更新します。

50%[===========================>________________________] 50,000 100.0K/s

これは、wget唯一の例ではありません。たとえば、パイプを使って何かを入力しlessてから終了すると、元のプロンプトは、以前に実行したコマンドの結果とともにそのまま残ります。まるで、一度も終了したことがなかったかのようです。

私の質問は、これは何と呼ばれているのか、どのように実装するのか、一度に 1 行しか機能しないのか、そしてこれを C で使用できるのか、ということです。

答え1

まず、あなたの質問は bash とは関係なく、ターミナルに関するものです。ターミナルはプログラムのテキストを表示するために応答しており、bash 自体は、起動後のプログラムを制御できません。

端末は色、フォント、カーソル位置などを制御するための制御シーケンスを提供します。標準化された端末シーケンスのリストについては、 http://www.termsys.demon.co.uk/vtansi.htm例えば

  • カーソルを行の先頭に置きます
  • その後行を削除します
  • 新しい行を書く

進行状況バーを作成します。

より高度な端末エスケープ シーケンスは通常、端末に依存します (例: Eterm または xterm でのみ動作します)。ncurses- エスケープ シーケンスを使用せずにターミナルで対話型プログラムを作成するためのプログラミング ライブラリです。

ターミナルシーケンスで既存の行を上書きする方法

echo long text
sleep 1
printf "\033[1A"  # move cursor one line up
printf "\033[K"   # delete till end of line
echo foo

ターミナルシーケンスなしで既存の行を上書きする方法

1 つの簡単な解決策は、最後に改行を記述するのではなく、キャリッジ リターンを記述することです。これにより、基本的にカーソルが行の先頭にリセットされます。例:

echo -n first 
sleep 1 
echo -ne "\rsecond"
echo

または\r改行キーを押すと、カーソルが行の先頭に移動し、行の内容を上書きできるようになります。

lessまたはのようなバッファを切り替えるvi

の動作はless、より高度なターミナル機能である代替画面によっても発生します。

VT102 モードでは、ウィンドウの表示領域と同じサイズの代替画面バッファを有効または無効にするエスケープ シーケンスがあります。有効にすると、現在の画面が保存され、代替画面に置き換えられます。ウィンドウの上部からスクロールして消えた行の保存は、通常の画面が復元されるまで無効になります。xterm の term-cap(5) エントリを使用すると、ビジュアル エディタ vi(1) で編集用に代替画面に切り替え、終了時に画面を復元できます。ポップアップ メニュー エントリを使用すると、カット アンド ペースト用に通常の画面と代替画面を簡単に切り替えることができます。

http://rosettacode.org/wiki/ターミナルコントロール/Preserve_screen自分で行う方法の例をいくつか挙げます。入力するまたはエスケープシーケンスを介して。

答え2

echo文字列に自動的に改行を追加する を使用する代わりに、 printf "%s\r" whatever-- を使用します。キャリッジ リターンはカーソルを現在の行の先頭に移動します。例:

seq 1 15 | while read num; do printf "%2d\r" $num; sleep 1; done; echo ""

答え3

このトピックを見つけた人には、こちらもぜひご覧になることをお勧めします。Bash プロンプト HOWTO - カーソルの移動

例:

- Position the Cursor:
  \033[<L>;<C>H
     Or
  \033[<L>;<C>f
  puts the cursor at line L and column C.
- Move the cursor up N lines:
  \033[<N>A
- Move the cursor down N lines:
  \033[<N>B
- Move the cursor forward N columns:
  \033[<N>C
- Move the cursor backward N columns:
  \033[<N>D

- Clear the screen, move to (0,0):
  \033[2J
- Erase to end of line:
  \033[K

- Save cursor position:
  \033[s
- Restore cursor position:
  \033[u

C での例をいくつか示します。

void saveCursorPosition() {
  printf("\033[s");
}

void restoreCursorPosition() {
  printf("\033[u");
}

void lineUP(short int times) {
  printf("\033[%iA", times);
}

void lineDown(short int times) {
  printf("\033[%iB", times);
}

プログラムサンプル:

#include <stdio.h>
#include <unistd.h>

void saveCursorPosition() {
  printf("\033[s");
}

void restoreCursorPosition() {
  printf("\033[u");
}

void lineUP(short int times) {
  printf("\033[%iA", times);
}

void moveCursorBackwards(short int times) {
  printf("\033[%iD", times);
}

void printMainText() {

  printf("\n ╔═══════════════════════════════╗");
  printf("\n ║                               ║");
  printf("\n ║ Progress Bar                  ║");
  printf("\n ║                               ║");
  printf("\n ║ []                            ║");
  printf("\n ║                               ║");
  printf("\n ║ Press Ctrl+C to close         ║");
  printf("\n ║                               ║");
  printf("\n ╚═══════════════════════════════╝\n");

}

int main(int argc, char **argv) {

  printMainText();

  for (int progress=0; progress <= 10; progress++) {

    saveCursorPosition();
    lineUP(5);

    printf("\r ║     [");
    fflush(stdout);

    for (int i=0; i<progress; i++) {
      printf("=>]");
      fflush(stdout);
      moveCursorBackwards(2);
    }

    moveCursorBackwards(progress + 5);

    printf("%i%%", progress * 10);
    fflush(stdout);

    restoreCursorPosition();

    sleep(1);

  }

  return 0;

}

関連情報