Timpa keluaran sebelumnya di Bash alih-alih menambahkannya

22

Untuk penghitung waktu bash, saya menggunakan kode ini:

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

Itu memberi saya sesuatu seperti itu

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

Apakah ada kemungkinan untuk mengganti nilai terakhir dari detik dengan yang terbaru sehingga output tidak menghasilkan jejak besar tetapi hitungan detik seperti waktu nyata pada satu posisi? (Semoga Anda mengerti apa yang saya maksud :))

NES
sumber
Mungkin ada cara untuk melakukan ini dengan watchperintah, meskipun saya tidak yakin bagaimana melakukannya.
AJMansfield

Jawaban:

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!"
aneeshep
sumber
2
Luar biasa, terima kasih banyak. Hanya satu pemberitahuan untuk pembaca lainnya. Agar sesuai dengan kode di atas Anda harus menggunakan echo -en "\ b \ b \ b" karena spasi.
NES
1
+1 Bagus, tapi ... di bawah 10 mulai memakan pesan ...
lepe
1
Yap, lebih baik digunakan \r. Lihat jawaban saya.
Mikel
3
Dari manual bash: The old format $[expression] is deprecated and will be removed in upcoming versions of bash.. Gunakan POSIX $((expression))atau ((perintah-sebagai gantinya. Misalnya sek=$(( sek - 1 ))atau (( sek = sek - 1 ))atau (( sek-- )).
geirha
17

Pada dasarnya sama dengan jawaban aneeshep, tetapi menggunakan Return ( \r) daripada Backspace ( \b) karena kita tidak tahu apakah panjangnya akan selalu sama, misalnya kapan $sek < 10.

Juga, yang pertama Anda echoharus gunakan $sek, bukan hard-code 60.

Akhirnya, perhatikan spasi setelah ....

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

Dengan bash Anda dapat menggunakan variabel khusus SECONDS.

#BASH
SECONDS=0;
while sleep .5 && ((SECONDS <= 60)); do 
    printf '\r%s: %2d' "One moment please" "$((60-SECONDS))"
done
printf '\n'
geirha
sumber
+1 Jawaban yang bagus, plus saya tidak pernah tahu tentang DETIK.
Mikel
Ini berfungsi, tetapi agak aneh melihat 'sleep .5' sebagai kondisi loop sementara yang diuji .. Namun saya lebih suka penggunaan $ DETIK, karena ini berhubungan dengan waktu nyata, (mis. Bukan waktu berlalu yang tetap antara tindakan yang memakan waktu, seperti dengan tidur 1) ... SECONDS Variabel ini meluas ke jumlah detik sejak shell dimulai. (jadi saya mungkin tidak akan mengaturnya ke 0 .. hanya mengujinya selama 60 detik lebih dari saat skrip dimulai) .. +1
Peter.O
Saya berkomentar lebih jauh, hanya karena saya pribadi tertarik dengan cara untuk mendapatkan coutntdown yang akurat ... Saya telah menguji $ DETIK dan tampaknya apakah diatur ke '0' atau tidak masih tergantung pada pecahan sistem saat ini kedua .. yaitu. Menyetelnya ke '0' dapat menghasilkan waktu 0,99 ... (hal yang sama berlaku jika dibiarkan apa adanya) .... Jadi, peluang rata-rata terbaiknya adalah hanya dalam 0,5 detik. .. Hanya komentar (itulah komentar untuk :)
Peter.O
@ fred.bear Nah, Anda tidak akan pernah benar-benar membuatnya akurat; beberapa proses lain dapat mulai memonopoli CPU dan / atau IO ketika hitungan mundur berjalan dan merusak akurasi apa pun yang Anda miliki sebelumnya. Apa yang dapat Anda lakukan adalah memilikinya menunggu setidaknya X jumlah waktu, dan memberikan beberapa hitungan mundur kasar jika diinginkan. Ketika sebuah program mengatakan "itu akan memakan waktu satu menit", apakah Anda berharap untuk mengambil tepat 1 menit, ke milidetik? atau untuk mengambil 1 menit, memberi atau mengambil beberapa detik?
geirha
@geirha ... Ya, variasi itu tidak masalah di 99% kasus, dan hebatnya Anda membuat saya sadar akan $ DETIK ... Saya baru menemukan batasnya ... Saya sudah bermain dengan variasi skrip Anda yang melaporkan waktu penyelesaian berdasarkan tidur 0,01 detik (ditambah, seperti yang Anda sebutkan, ada keterlambatan sistem eksternal) namun hanya mencetak ketika klik kedua berakhir, tetapi kemudian saya menemukan 'detik' pertama mencetak terlalu cepat (dalam satu kasus, tepat setelah 0,01 detik pertama) .. Itu semua adalah bagian dari kurva bash learning saya ... Saya suka jawaban Anda, itulah mengapa saya telah melihatnya lebih dalam.
Peter.O
3

Selain \ratau \bpendekatan, dimungkinkan untuk menggunakan \033[2K karakter kontrol , yang memberitahu terminal untuk menghapus seluruh baris. Keuntungan dari ini dibandingkan \badalah bahwa Anda tidak harus mencocokkan jumlah \bdengan jumlah karakter yang ingin Anda hapus, dan dibandingkan dengan itu \rtidak akan ada karakter yang mencuat di layar jika baris baru lebih pendek daripada yang lama satu.

Di bawah ini adalah contoh bagaimana hal itu dapat diterapkan pada pertanyaan ini, dan di sini adalah contoh aplikasi terkait untuk membuat output yang mirip dengan pesan boot. Dalam contoh khusus ini, timer akan hilang begitu 0 detik tercapai dan garis timer akan diganti dengan "Siap!" frasa.

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

Alternatif lain adalah menggunakan dialogperintah untuk membuat dialog sederhana di command-line. Dialog akan tetap di layar selama durasi timer dan diperbarui dengan loop, dan pada saat itu selesai - timer akan diganti dengan pesan "Ready! Tekan untuk keluar" dengan cara yang mulus:

#!/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
Sergiy Kolodyazhnyy
sumber
dialogtidak ada di mac: /
Ricky Levi
1

Inilah yang saya dapatkan setelah membaca di sini dan sedikit lagi, liner:

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

Lebih mudah dibaca:

#!/bin/bash
SEC=101

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

Di mana SEC dapat diatur ke bilangan bulat positif apa pun dan printf akan menangani padding yang sesuai. Diuji di bawah Ubuntu dan cygwin.

Rubah
sumber
1

Dapat mencapainya dengan menempatkan carriage return \r.

Dalam satu baris kode, dimungkinkan dengan echo -ne

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

atau dengan printf

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

Penghitung waktu mundur:

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

dan stopwatch 'normal' adalah:

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

kontrol + c untuk berhenti

Biasa
sumber
Ini tidak menjawab pertanyaan tentang menimpa teks
Jeremy Kerr