Làm thế nào để xóa và thay thế dòng cuối cùng trong thiết bị đầu cuối bằng cách sử dụng bash?


99

Tôi muốn triển khai thanh tiến trình hiển thị số giây đã trôi qua trong bash. Đối với điều này, tôi cần xóa dòng cuối cùng hiển thị trên màn hình (lệnh "clear" xóa tất cả màn hình, nhưng tôi chỉ cần xóa dòng của thanh tiến trình và thay thế bằng thông tin mới).

Kết quả cuối cùng sẽ giống như sau:

$ Elapsed time 5 seconds

Sau đó, sau 10 giây, tôi muốn thay thế câu này (ở cùng vị trí trong màn hình) bằng:

$ Elapsed time 15 seconds

Câu trả lời:


116

lặp lại dấu xuống dòng với \ r

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

từ tiếng vang của người đàn ông:

-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"; doneđể tránh cuộc gọi seq :-)
Douglas Leeder

Điều này không chính xác những gì tôi cần, cảm ơn !! Và việc sử dụng các lệnh seq thực sự đóng băng termianl :-)
Debugger

11
Khi bạn sử dụng những thứ như "for i in $ (...)" hoặc "for i in {1..N}", bạn đang tạo tất cả các phần tử trước khi lặp lại, điều đó rất kém hiệu quả đối với các đầu vào lớn. Bạn có thể tận dụng các đường ống: "seq 1 100000 | while read i; do ..." hoặc sử dụng vòng lặp for kiểu bash c: "for ((i = 0 ;; i ++)); do ..."
tokland

Cảm ơn Douglas và tokland - mặc dù việc sản xuất theo trình tự không trực tiếp là một phần của câu hỏi, tôi đã đổi sang đường ống hiệu quả hơn của tokland
Ken

1
Máy in ma trận của tôi hoàn toàn làm hỏng giấy của tôi. Nó tiếp tục làm kẹt các dấu chấm trên cùng một mảnh giấy mà không còn ở đó nữa, chương trình này chạy trong bao lâu?
Rob

185

Dấu xuống dòng tự nó chỉ di chuyển con trỏ đến đầu dòng. Điều đó là OK nếu mỗi dòng đầu ra mới ít nhất dài bằng dòng trước đó, nhưng nếu dòng mới ngắn hơn, dòng trước đó sẽ không bị ghi đè hoàn toàn, ví dụ:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r0123456789"
0123456789klmnopqrstuvwxyz

Để thực sự xóa dòng cho văn bản mới, bạn có thể thêm \033[Ksau \r:

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

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


3
Điều này thực sự hoạt động tốt trong môi trường của tôi. Bất kỳ kiến ​​thức về khả năng tương thích?
Alexander Olsson

21
Trong Bash ít nhất có thể được rút ngắn thành \e[Kthay vì \033[K.
Dave James Miller

Câu trả lời chính xác! Hoạt động hoàn hảo bằng cách sử dụng đặt từ ruby. Bây giờ là một phần của bộ công cụ của tôi.
phatmann

@ Nhà phát triển Pixel: Cảm ơn bạn đã cố gắng cải thiện nó, nhưng chỉnh sửa của bạn không chính xác. Việc trả về phải xảy ra trước tiên để di chuyển con trỏ đến đầu dòng, sau đó việc hủy sẽ xóa mọi thứ từ vị trí con trỏ đó đến cuối, để trống toàn bộ dòng, đó là ý định. Bạn đặt chúng ở đầu mỗi dòng mới, tương tự như câu trả lời của Ken, để nó được viết trên một dòng trống.
Derek Veit

1
Chỉ đối với bản ghi, người ta cũng có thể làm \033[Gio \rtrước đó \033[Kmặc dù rõ ràng \rlà đơn giản hơn nhiều. Ngoài ra, hidden-island.net/xterm/ctlseqs/ctlseqs.html cung cấp nhiều thông tin chi tiết hơn Wikipedia và là của nhà phát triển xterm.
jamadagni

19

Câu trả lời của Derek Veit hoạt động tốt miễn là chiều dài đường thẳng không bao giờ vượt quá chiều rộng thiết bị đầu cuối. Nếu không đúng như vậy, đoạn mã sau sẽ ngăn đầu ra rác:

trước khi dòng được viết lần đầu tiên, hãy làm

tput sc

sẽ lưu vị trí con trỏ hiện tại. Bây giờ bất cứ khi nào bạn muốn in dòng của mình, hãy sử dụng

tput rc
tput ed
echo "your stuff here"

để quay lại vị trí con trỏ đã lưu trước tiên, sau đó xóa màn hình từ con trỏ xuống dưới cùng và cuối cùng là ghi kết quả đầu ra.


Wierd, điều này không có gì trong terminator. Bạn có biết nếu có những hạn chế về khả năng tương thích?
Jamie Pate

1
Lưu ý cho Cygwin: Bạn cần cài đặt gói "ncurses" để sử dụng "tput".
Jack Miller

Hm ... Có vẻ như không hoạt động nếu nhiều hơn một dòng ở đầu ra. Điều này không hoạt động: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 Bạn cần sử dụng tput edhơn là tput el. Ví dụ của @ Um được sử dụng ed(có lẽ anh ấy đã sửa nó sau khi bạn nhận xét).
studgeek

FYI, đây là danh sách các lệnh tput Màu sắc và Cursor Phong trào Với tput
studgeek

12

Phương thức \ 033 không hoạt động với tôi. Phương thức \ r hoạt động nhưng nó không thực sự xóa bất cứ thứ gì, chỉ đặt con trỏ ở đầu dòng. Vì vậy, nếu chuỗi mới ngắn hơn chuỗi cũ, bạn có thể thấy phần văn bản còn sót lại ở cuối dòng. Cuối cùng thì tput là cách tốt nhất để đi. Nó có các công dụng khác ngoài công cụ con trỏ cộng với nó được cài đặt sẵn trong nhiều bản phân phối Linux & BSD nên nó sẽ có sẵn cho hầu hết người dùng 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

Đây là một tập lệnh đếm ngược nhỏ để chơi với:

#!/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"

Đó là xóa toàn bộ dòng trên thực tế, vấn đề nó twinkles quá nhiều cho tôi: '(
smarber

5

Trong trường hợp đầu ra tiến trình là nhiều dòng hoặc tập lệnh đã in ký tự dòng mới, bạn có thể nhảy lên các dòng như:

printf "\033[5A"

điều này sẽ làm cho con trỏ nhảy lên 5 dòng. Sau đó, bạn có thể ghi đè bất cứ thứ gì bạn cần.

Nếu điều đó không hiệu quả, bạn có thể thử printf "\e[5A"hoặc echo -e "\033[5A", cách này sẽ có tác dụng tương tự.

Về cơ bản, với trình tự thoát bạn có thể kiểm soát hầu hết mọi thứ trong màn hình.


Tương đương di động của điều này là tput cuu 5, trong đó 5 là số hàng ( cuuđể di chuyển lên, cuddi chuyển xuống).
Maëlan

4

Sử dụng ký tự xuống dòng:

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

Câu trả lời này là cách đơn giản nhất. Bạn thậm chí có thể đạt được hiệu quả tương tự sử dụng: printf "Foo \ rBar"
lee8oi

1

Có thể đạt được nó bằng cách đặt ký tự xuống dòng \r.

Trong một dòng mã với printf

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

Hoặc với echo -ne

for i in {10..1}; do echo -ne "Counting down: $i\r" && sleep 1; done
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.