Belum pernah melihat yang serupa dan semua fungsi khusus di sini tampaknya fokus pada rendering saja jadi ... solusi patuh POSIX saya yang sangat sederhana di bawah ini dengan penjelasan langkah demi langkah karena pertanyaan ini tidak sepele.
TL; DR
Rendering progress bar sangat mudah. Memperkirakan berapa banyak yang harus disajikan adalah masalah yang berbeda. Ini adalah cara merender (menghidupkan) bilah kemajuan - Anda dapat menyalin & menempelkan contoh ini ke file dan menjalankannya:
#!/bin/sh
BAR='####################' # this is full bar, e.g. 20 chars
for i in {1..20}; do
echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
sleep .1 # wait 100ms between "frames"
done
{1..20}
- nilai dari 1 hingga 20
echo -n
- cetak tanpa garis baru di akhir
echo -e
- menafsirkan karakter khusus saat mencetak
"\r"
- carriage return, char khusus untuk kembali ke awal baris
Anda dapat membuatnya menyajikan konten apa pun dengan kecepatan apa pun sehingga metode ini sangat universal, misalnya sering digunakan untuk visualisasi "peretasan" dalam film konyol, tanpa bercanda.
Jawaban penuh
Daging masalahnya adalah bagaimana menentukan $i
nilai, yaitu berapa banyak progress bar untuk ditampilkan. Dalam contoh di atas saya hanya membiarkannya bertambah dalam for
lingkaran untuk menggambarkan prinsip tetapi aplikasi kehidupan nyata akan menggunakan loop tak terbatas dan menghitung $i
variabel pada setiap iterasi. Untuk membuat perhitungan tersebut diperlukan bahan-bahan berikut:
- berapa banyak pekerjaan yang harus dilakukan
- berapa banyak pekerjaan yang telah dilakukan sejauh ini
Dalam kasus cp
itu perlu ukuran file sumber dan ukuran file target:
#!/bin/sh
$src=/path/to/source/file
$tgt=/path/to/target/file
cp "$src" "$tgt" & # the & forks the `cp` process so the rest
# of the code runs without waiting (async)
BAR='####################'
src_size=$(stat -c%s "$src") # how much there is to do
while true; do
tgt_size=$(stat -c%s "$tgt") # how much has been done so far
i=$(( $tgt_size * 20 / $src_size ))
echo -ne "\r${BAR:0:$i}"
if [ $tgt_size == $src_size ]; then
echo "" # add a new line at the end
break; # break the loop
fi
sleep .1
done
stat
- periksa statistik file
-c
- mengembalikan nilai yang diformat
%s
- ukuran total
Dalam hal operasi seperti membongkar file, menghitung ukuran sumber sedikit lebih sulit tetapi masih semudah mendapatkan ukuran file yang tidak terkompresi:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l
- Menampilkan info tentang arsip zip
tail -n1
- bekerja dengan 1 baris dari bawah
tr -s ' '
- menerjemahkan beberapa spasi menjadi satu (tekan)
cut -d' ' -f3
- potong kolom ke-3 yang dibatasi ruang
Tapi inilah masalahnya. Solusi ini semakin tidak umum. Semua perhitungan kemajuan aktual terikat erat dengan domain yang Anda coba visualisasikan, apakah itu operasi file tunggal, penghitung waktu mundur, peningkatan jumlah file dalam direktori, operasi pada banyak file, dll., Oleh karena itu, tidak bisa digunakan kembali. Satu-satunya bagian yang dapat digunakan kembali adalah rendering progress bar. Untuk menggunakannya kembali, Anda perlu mengabstraksikan dan menyimpannya dalam file (mis. /usr/lib/progress_bar.sh
), Lalu mendefinisikan fungsi yang menghitung nilai input khusus untuk domain Anda. Beginilah tampilan kode yang digeneralisasi (saya juga membuat $BAR
dinamis karena orang memintanya, sisanya harus jelas sekarang):
#!/bin/sh
BAR_length=50
BAR_character='#'
BAR=$(printf "%${BAR_length}s" | tr ' ' $BAR_character)
work_todo=$(get_work_todo) # how much there is to do
while true; do
work_done=$(get_work_done) # how much has been done so far
i=$(( $work_done * $BAR_length / $work_todo ))
echo -ne "\r${BAR:0:$i}"
if [ $work_done == $work_todo ]; then
echo ""
break;
fi
sleep .1
done
printf
- bawaan untuk mencetak barang dalam format yang diberikan
printf '%50s'
- cetak apa-apa, pad dengan 50 spasi
tr ' ' '#'
- menerjemahkan setiap spasi menjadi tanda pagar
Dan inilah cara Anda menggunakannya:
#!/bin/sh
src=/path/to/source/file
tgt=/path/to/target/file
function get_work_todo() {
echo $(stat -c%s "$src")
}
function get_work_done() {
[ -e "$tgt" ] && # if target file exists
echo $(stat -c%s "$tgt") || # echo its size, else
echo 0 # echo zero
}
cp "$src" "$tgt" & # copy in the background
source /usr/lib/progress_bar.sh # execute the progress bar
Jelas itu dapat dibungkus dalam suatu fungsi, ditulis ulang untuk bekerja dengan aliran pipa, ditulis ulang ke bahasa lain, apa pun racun Anda.