Nie widziałem nic podobnego, a wszystkie niestandardowe funkcje wydają się koncentrować na samym renderowaniu, więc ... moje bardzo proste rozwiązanie zgodne z POSIX poniżej z objaśnieniami krok po kroku, ponieważ to pytanie nie jest trywialne.
TL; DR
Renderowanie paska postępu jest bardzo łatwe. Szacowanie, ile z tego powinno się renderować, to inna sprawa. Oto jak renderować (animować) pasek postępu - możesz skopiować i wkleić ten przykład do pliku i uruchomić go:
#!/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}
- wartości od 1 do 20
echo -n
- drukuj bez nowej linii na końcu
echo -e
- interpretować znaki specjalne podczas drukowania
"\r"
- powrót karetki, specjalny znak powrotu do początku linii
Możesz sprawić, że będzie renderować dowolną zawartość przy dowolnej prędkości, więc ta metoda jest bardzo uniwersalna, np. Często używana do wizualizacji „hakowania” w niemądrych filmach, bez żartów.
Pełna odpowiedź
Istotą problemu jest to, jak określić $i
wartość, tj. Jaka część paska postępu ma zostać wyświetlona. W powyższym przykładzie po prostu pozwoliłem inkrementacji w for
pętli, aby zilustrować zasadę, ale aplikacja z prawdziwego życia użyłaby nieskończonej pętli i obliczyłaby $i
zmienną przy każdej iteracji. Do wykonania wspomnianych obliczeń potrzebne są następujące składniki:
- ile pracy trzeba zrobić
- ile pracy wykonano do tej pory
W razie cp
potrzeby rozmiar pliku źródłowego i rozmiar pliku docelowego:
#!/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
- sprawdź statystyki pliku
-c
- zwraca sformatowaną wartość
%s
- całkowity rozmiar
W przypadku operacji takich jak rozpakowywanie pliku, obliczenie rozmiaru źródła jest nieco trudniejsze, ale nadal tak proste, jak uzyskanie rozmiaru nieskompresowanego pliku:
#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
gzip -l
- wyświetla informacje o archiwum zip
tail -n1
- praca z 1 linią od dołu
tr -s ' '
- przetłumacz wiele spacji na jeden (ściśnij je)
cut -d' ' -f3
- wytnij 3. rozdzielaną spacjami kolumnę
Oto sedno problemu. To rozwiązanie jest coraz mniej ogólne. Wszystkie obliczenia rzeczywistego postępu są ściśle powiązane z domeną, którą próbujesz sobie wyobrazić, czy jest to pojedyncza operacja na pliku, odliczanie czasu, rosnąca liczba plików w katalogu, operacja na wielu plikach itp., Dlatego nie można ponownie użyć. Jedyną częścią wielokrotnego użytku jest renderowanie paska postępu. Aby go ponownie użyć, musisz go wyodrębnić i zapisać w pliku (np. /usr/lib/progress_bar.sh
), A następnie zdefiniować funkcje, które obliczają wartości wejściowe specyficzne dla Twojej domeny. Tak mógłby wyglądać uogólniony kod (wprowadziłem też $BAR
dynamikę, ponieważ ludzie o to prosili, reszta powinna być teraz jasna):
#!/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
- wbudowane do drukowania rzeczy w danym formacie
printf '%50s'
- nic nie drukuj, wypełnij go 50 spacjami
tr ' ' '#'
- przetłumacz każdą spację na znak skrótu
I tak byś tego użył:
#!/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
Oczywiście może być zawinięty w funkcję, przepisany do pracy z potokami strumieniowymi, przepisany na inny język, bez względu na twoją truciznę.