bashを使用してターミナルの最後の行を削除して置き換える方法は?


99

bashに経過秒数を表示する進行状況バーを実装したいと思います。これを行うには、画面に表示されている最後の行を消去する必要があります(コマンド「clear」はすべての画面を消去しますが、プログレスバーの行のみを消去して、新しい情報に置き換える必要があります)。

最終結果は次のようになります。

$ Elapsed time 5 seconds

次に、10秒後、この文を(画面内の同じ位置にある)置き換えます。

$ Elapsed time 15 seconds

回答:


116

キャリッジリターンを\ rでエコー

seq 1 1000000 | while read i; do echo -en "\r$i"; done

男のエコーから:

-n     do not output the trailing newline
-e     enable interpretation of backslash escapes

\r     carriage return

19
for i in {1..100000}; do echo -en "\r$i"; doneseqコールを回避するには:-)
ダグラスリーダー

これは私が必要とするものを正確に行います、ありがとう!! そして、seqコマンドを使用すると、実際にtermianlがフリーズします:-)
デバッガ

11
"for i in $(...)"や "for i in {1..N}"のようなものを使用すると、反復前にすべての要素が生成されます。これは、大きな入力に対しては非常に非効率的です。パイプを利用する: "seq 1 100000 | while read i; do ..."またはbash cスタイルのforループを使用する: "for((i = 0 ;; i ++)); do ..."
tokland

Douglasとtoklandに感謝します-シーケンスの生産は直接質問の一部ではありませんでしたが、私はtoklandのより効率的なパイプに変更しました
Ken

1
マトリックスプリンターが用紙を完全に乱雑にしています。それはもはや存在しない同じ紙にドットを妨害し続けます、このプログラムはどれくらいの期間実行しますか?
Rob

185

キャリッジリターン自体は、カーソルを行の先頭に移動するだけです。出力の新しい各行が少なくとも前の行と同じ長さであれば問題ありませんが、新しい行が短いと、前の行は完全に上書きされません。たとえば、次のようになります。

$ echo -e "abcdefghijklmnopqrstuvwxyz\r0123456789"
0123456789klmnopqrstuvwxyz

新しいテキストの行を実際にクリアするには、次のように追加\033[Kします\r

$ echo -e "abcdefghijklmnopqrstuvwxyz\r\033[K0123456789"
0123456789

http://en.wikipedia.org/wiki/ANSI_escape_code


3
これは私の環境で本当にうまくいきます。互換性の知識はありますか?
Alexander Olsson

21
Bashでは、少なくともの\e[K代わりに短縮できます\033[K
Dave James Miller

正解です。ルビーからのプットを使用して完全に動作します。今私のツールキットの一部です。
ファットマン

@The Pixel Developer:改善を試みていただきありがとうございます。編集内容が正しくありませんでした。カーソルを行の先頭に移動するには、最初にReturnを実行する必要があります。次に、Killはそのカーソル位置から最後まですべてをクリアし、行全体を空白のままにします。これは意図です。それらを、Kenの回答と同様に、新しい各行の先頭に配置して、空の行に書き込まれるようにします。
Derek Veit

1
念のため、前に\033[Gioを実行することもできますが、明らかにはるかに簡単です。また、invisible-island.net / xterm / ctlseqs / ctlseqs.htmlは、ウィキペディアより詳細で、xterm開発者によるものです。\r\033[K\r
jamadagni 2016年

19

Derek Veitの回答は、行の長さが端末の幅を超えない限り有効です。そうでない場合は、次のコードでジャンク出力を防止します。

行が初めて書かれる前に、

tput sc

現在のカーソル位置を保存します。行を印刷したいときはいつでも、

tput rc
tput ed
echo "your stuff here"

最初に保存されたカーソル位置に戻り、次に画面をカーソルから下に消去し、最後に出力を書き込みます。


奇妙なことに、これはターミネーターでは何もしません。互換性の制限があるかどうか知っていますか?
Jamie Pate

1
Cygwinに関する注意:「tput」を使用するには、パッケージ「ncurses」をインストールする必要があります。
Jack Miller

うーん...出力に複数の行がある場合は機能しないようです。これは機能しません:tput sc # save cursor echo '' > sessions.log.json while [ 1 ]; do curl 'http://localhost/jolokia/read/*:type=Manager,*/activeSessions,maxActiveSessions' >> sessions.log.json echo '' >> sessions.log.json cat sessions.log.json | jq '.' tput rc;tput el # rc = restore cursor, el = erase to end of line sleep 1 done
Nux

@Nux tput edではなくを使用する必要がありますtput el。@Umの例が使用さedれています(コメントした後で修正した可能性があります)。
studgeek 2017年

参考までに、tputコマンドの色とカーソル移動
studgeek

12

\ 033の方法ではうまくいきませんでした。\ rメソッドは機能しますが、実際には何も消去せず、カーソルを行の先頭に置きます。したがって、新しい文字列が古い文字列よりも短い場合は、行末に残ったテキストが表示されます。結局、tputが最善の方法でした。カーソルのほかに他の用途があり、多くのLinuxおよびBSDディストリビューションにプリインストールされているため、ほとんどのbashユーザーが利用できます。

#/bin/bash
tput sc # save cursor
printf "Something that I made up for this string"
sleep 1
tput rc;tput el # rc = restore cursor, el = erase to end of line
printf "Another message for testing"
sleep 1
tput rc;tput el
printf "Yet another one"
sleep 1
tput rc;tput el

以下は、小さなカウントダウンスクリプトです。

#!/bin/bash
timeout () {
    tput sc
    time=$1; while [ $time -ge 0 ]; do
        tput rc; tput el
        printf "$2" $time
        ((time--))
        sleep 1
    done
    tput rc; tput ed;
}

timeout 10 "Self-destructing in %s"

これで実際にすべての行がクリアされ、問題が多すぎます: '(
smarber

5

進行状況の出力が複数行である場合、またはスクリプトがすでに改行文字を印刷している場合は、次のような行にジャンプできます。

printf "\033[5A"

カーソルが5行上にジャンプします。その後、必要なものを上書きできます。

それがうまくいかない場合は、printf "\e[5A"またはを試してみてくださいecho -e "\033[5A"。同じ効果があります。

基本的に、エスケープシーケンスを使用すると、画面内のほぼすべてを制御できます。


これと同等の移植性はtput cuu 5で、5は行数です(上cuuに移動する、下cudに移動する)。
Maëlan

4

復帰文字を使用します。

echo -e "Foo\rBar" # Will print "Bar"

この答えが最も簡単な方法です。次のコマンドを使用しても同じ効果を得ることができます。printf "Foo \ rBar"
lee8oi

1

キャリッジリターンを配置することでそれを実現できます。 \r

1行のコードで printf

for i in {10..1}; do printf "Counting down: $i\r" && sleep 1; done

または echo -ne

for i in {10..1}; do echo -ne "Counting down: $i\r" && sleep 1; done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.