因此,當wget
取得網頁時,它會顯示一個狀態欄,指示檔案的下載量。它看起來像這樣:
25%[=============>______________________________________] 25,000 100.0K/s
(下劃線是空格;我只是不知道如何在其中獲得多個連續空格)
但是,它不會向 stdout 寫入另一行並添加另一個進度條,而是對其進行更新,如下所示:
50%[===========================>________________________] 50,000 100.0K/s
這也不wget
是唯一的例子。例如,當您透過管道輸入某些內容less
然後退出時,原始提示符號以及您之前執行的任何命令的結果仍然存在。就好像你從未離開過。
所以,我的問題是,這叫什麼,我要如何實現它,它一次只能用於一行嗎?
答案1
首先,你的問題與 bash 無關,而是與終端有關。終端正在回應顯示程式的文本,而 bash 本身在程式啟動後無法控制它們。
終端提供控制序列來控制顏色、字體、遊標位置等。有關標準化終端序列的列表,請查看 http://www.termsys.demon.co.uk/vtansi.htm例如你可以
- 將遊標置於行首
- 之後刪除該行
- 寫一個新行
建立進度條。
更高級的終端轉義序列通常與終端相關,例如僅適用於 Eterm 或 xterm。恩詛咒- 是一個程式庫,用於建立與終端的互動式程序,這樣您就不必使用轉義序列。
如何用終端序列覆蓋現有行
echo long text
sleep 1
printf "\033[1A" # move cursor one line up
printf "\033[K" # delete till end of line
echo foo
如何在沒有終端序列的情況下覆蓋現有行
一個簡單的解決方案是不在末尾寫入換行符,而是寫入回車符,基本上將遊標重置到行的開頭,例如:
echo -n first
sleep 1
echo -ne "\rsecond"
echo
或 回車符\r
會將遊標置於行的開頭,並允許您覆寫該行的內容。
在緩衝區之間切換,如less
或vi
的行為less
也是由於更高級的終端功能,即備用螢幕:
在 VT102 模式下,有轉義序列用於啟動和停用備用螢幕緩衝區,該緩衝區的大小與視窗的顯示區域相同。啟動後,當前螢幕將被儲存並替換為備用螢幕。在恢復正常畫面之前,將停用已儲存從視窗頂部滾動的行。 xterm 的 termcap(5) 條目允許可視化編輯器 vi(1) 切換到備用畫面進行編輯並在退出時恢復畫面。彈出式選單項目可以輕鬆地在正常螢幕和備用螢幕之間切換以進行剪下和貼上。
http://rosettacode.org/wiki/Terminal_control/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;
}