Ghi đè đầu ra trước đó trong Bash thay vì nối thêm nó


22

Đối với một bộ đếm thời gian bash tôi sử dụng mã này:

#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek "  
sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

Điều đó mang lại cho tôi một cái gì đó như thế

One Moment please: 60 59 58 57 56 55 ...

Có khả năng thay thế giá trị cuối cùng của giây bằng lần gần nhất để đầu ra không tạo ra dấu vết lớn mà đếm ngược giây như thời gian thực tại một vị trí không? (Hy vọng bạn hiểu ý của tôi :))


Có thể có một cách để làm điều này với watchlệnh, mặc dù tôi không chắc chắn chính xác làm thế nào để làm điều đó.
AJMansfield

Câu trả lời:


14
#!/bin/bash
sek=60
echo "60 Seconds Wait!"
echo -n "One Moment please "
while [ $sek -ge 1 ]
do
   echo -n "$sek" 

sleep 1
   sek=$[$sek-1]
   echo -en "\b\b"
done
echo
echo "ready!"

2
Thật tuyệt vời, cảm ơn rất nhiều. Chỉ cần một thông báo cho các độc giả khác. Để phù hợp với mã ở trên, bạn phải sử dụng echo -en "\ b \ b \ b" vì không gian.
NES

1
+1 Đẹp, nhưng ... dưới 10 thì nó bắt đầu ăn tin nhắn ...
lepe

1
Đúng, tốt hơn để sử dụng \r. Xem câu trả lời của tôi.
Mikel

3
Từ hướng dẫn của bash : The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Sử dụng POSIX $((expression))hoặc ((-command thay thế. Ví dụ sek=$(( sek - 1 ))hoặc (( sek = sek - 1 ))hoặc (( sek-- )).
geirha

17

Về cơ bản giống như câu trả lời của aneeshep, nhưng sử dụng Return ( \r) thay vì Backspace ( \b) vì chúng ta không biết liệu độ dài sẽ luôn giống nhau hay không, ví dụ như khi nào $sek < 10.

Ngoài ra, đầu tiên của bạn echonên sử dụng $sek, không phải mã cứng 60.

Cuối cùng, lưu ý không gian sau ....

#!/bin/bash
sek=60
echo "$sek Seconds Wait!"
while [ $sek -ge 1 ]
do
   echo -ne "One Moment please $sek ... \r"
   sleep 1
   sek=$[$sek-1]
done
echo
echo "ready!"

7

Với bash bạn có thể sử dụng biến đặc biệt SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'

+1 Câu trả lời hay, cộng với tôi chưa bao giờ biết về GIÂY.
Mikel

Nó hoạt động, nhưng hơi lạ khi thấy 'ngủ .5' như một điều kiện vòng lặp được thử nghiệm .. Howerver Tôi đặc biệt thích sử dụng $ SECONDS, vì nó liên quan đến thời gian thực, (không phải là thời gian trôi qua cố định giữa hành động tốn thời gian, như với giấc ngủ 1) ... SECONDS Biến này mở rộng đến số giây kể từ khi vỏ được bắt đầu. (vì vậy tôi có thể sẽ không đặt nó thành 0 .. chỉ cần kiểm tra thêm 60 giây nữa kể từ khi tập lệnh bắt đầu) .. +1
Peter.O

Tôi đang bình luận thêm, chỉ vì cá nhân tôi quan tâm đến cách để có được một coutntdown chính xác ... Tôi đã kiểm tra $ GIÂY và dường như nó có được đặt thành '0' hay không vẫn phụ thuộc vào hệ thống phân số hiện tại thứ hai .. tức là. Đặt nó thành '0' có thể dẫn đến thời gian là 0,99 ... (điều tương tự cũng đúng nếu nó được giữ nguyên) .... Vì vậy, cơ hội trung bình tốt nhất của nó là chỉ trong 0,55 giây. .. Chỉ là một nhận xét (đó là những gì bình luận dành cho :)
Peter.O

@ fred.bear Vâng, bạn sẽ không bao giờ thực sự hiểu chính xác; một số quy trình khác có thể bắt đầu ăn mòn CPU và / hoặc IO trong khi quá trình đếm ngược đang diễn ra và phá hỏng bất kỳ độ chính xác nào bạn có trước đó. Những gì bạn có thể làm là để nó chờ ít nhất X thời gian và đưa ra một số đếm ngược thô nếu muốn. Khi một chương trình nói "nó sẽ mất một phút", bạn có mong đợi nó sẽ mất đúng 1 phút, đến mili giây không? hoặc mất 1 phút, cho hay mất vài giây?
geirha

@geirha ... Vâng, sự khác biệt đó không thành vấn đề trong 99% trường hợp và thật tuyệt vời, bạn đã nhận ra $ SECONDS ... Tôi chỉ tìm thấy giới hạn của nó ... Tôi đã chơi với một biến thể của tập lệnh báo cáo thời gian hoàn thành dựa trên giấc ngủ 0,01 giây (cộng với, như bạn đã đề cập, bất kỳ độ trễ hệ thống bên ngoài nào) chỉ in khi lần thứ hai nhấp qua, nhưng sau đó tôi phát hiện ra 'giây' đầu tiên đã in quá sớm (trong một trường hợp, chỉ sau 0,01 giây đầu tiên) .. Đó là tất cả các phần trong quá trình học tập bash của tôi ... Tôi thích câu trả lời của bạn, đó là lý do tại sao tôi đã nhìn sâu hơn vào nó.
Peter.O

3

Ngoài \rhoặc \btiếp cận, có thể sử dụng \033[2K ký tự điều khiển , thông báo cho thiết bị đầu cuối để xóa toàn bộ dòng. Ưu điểm của điều này so với \bviệc bạn không phải khớp số lượng \bvới số lượng ký tự bạn muốn xóa và so với \rsẽ không có ký tự nào xuất hiện trên màn hình nếu dòng mới ngắn hơn dòng cũ một.

Dưới đây là ví dụ về cách áp dụng nó cho câu hỏi này và đây là một ví dụ về ứng dụng liên quan để tạo đầu ra tương tự như thông điệp khởi động. Trong ví dụ cụ thể này, bộ hẹn giờ sẽ biến mất sau khi đạt đến giây thứ 0 và dòng hẹn giờ sẽ được thay thế bằng "Sẵn sàng!" cụm từ.

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    printf "One moment please: %d" "$sek"
    sleep 1
    printf "\r%b" "\033[2K"
done
echo "Ready!"

Một cách khác là sử dụng dialoglệnh để tạo các hộp thoại đơn giản trong dòng lệnh. Hộp thoại sẽ duy trì trên màn hình trong suốt thời gian của bộ hẹn giờ và cập nhật theo vòng lặp và đến khi hoàn thành - bộ hẹn giờ sẽ được thay thế bằng thông báo "Sẵn sàng! Nhấn để thoát" một cách liền mạch:

#!/bin/bash
sek=60
echo "60 Seconds"

while ((sek--)); do
    echo "$sek" | dialog --progressbox "Please wait" 10 25
    sleep 1
done
dialog --msgbox "Ready! Press <OK> to finish" 10 25

dialogkhông tồn tại trên mac: /
Ricky Levi

1

Đây là những gì tôi nghĩ ra sau khi đọc ở đây và một chút nữa, một trong những:

SEC=101;for i in `seq $SEC -1 1`;do printf "\rNext in: %`expr length $SEC`ds" "$i";sleep 1;done;echo

Dễ đọc hơn:

#!/bin/bash
SEC=101

for i in `seq $SEC -1 1`;do
        printf "\rNext in: %`expr length $SEC`ds" "$i";
        sleep 1;
done
echo

Trong đó SEC có thể được đặt thành bất kỳ số nguyên dương nào và printf sẽ đảm nhiệm phần đệm phù hợp. Đã thử nghiệm dưới Ubuntu và cygwin.


1

Có thể đạt được nó bằng cách đặt trở lại vận chuyển \r.

Trong một dòng mã, có thể với echo -ne

for i in {60..1}; do echo -ne "One moment please: $i\r" && sleep 1; done

Hoặc với printf

for i in {60..1}; do printf "One moment please: $i\r" && sleep 1; done

-2

Đồng hồ đếm ngược:

MIN=1 && for i in $(seq $(($MIN*60)) -1 1); do echo -n "$i, "; sleep 1; done; echo -e "nnMessage"

và đồng hồ bấm giờ 'bình thường' là:

START=$( date +%s ); while true; do CURRENT=$( date +%s ) ; echo $(( CURRENT-START )) ; sleep 1 ; echo -n  ; done

kiểm soát + c để dừng


Điều này không trả lời câu hỏi về văn bản ghi đè
Jeremy Kerr
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.