Jak dodać pasek postępu do skryptu powłoki?

413

Podczas wykonywania skryptów w bash lub innej powłoce w * NIX, podczas uruchamiania polecenia, które zajmie więcej niż kilka sekund, potrzebny jest pasek postępu.

Na przykład, kopiowanie dużego pliku, otwieranie dużego pliku tar.

W jaki sposób zaleca się dodawanie pasków postępu do skryptów powłoki?

Tom Feiner
źródło
Zobacz także stackoverflow.com/questions/12498304/ ... przykłady logiki sterowania (tło zadania i rób coś, aż się skończy).
tripleee,
1
Istnieje zestaw wymagań, które często uważamy za przydatne podczas pisania skryptów. logowanie, wyświetlanie postępu, kolorów, fantazyjnych wyników itp. Zawsze czułem, że powinien istnieć jakiś prosty framework do tworzenia skryptów. W końcu zdecydowałem się na wdrożenie jednego, ponieważ nie mogłem go znaleźć. Może ci się to przydać. To czysta bash, to znaczy Just Bash. github.com/SumuduLansakara/JustBash
Sumudu
Czy nie należy tego przenieść na unix.stackexchange.com ?
Ethan

Odpowiedzi:

685

Możesz to zaimplementować, nadpisując linię. Użyj, \raby wrócić do początku linii bez pisania \nna terminalu.

Napisz, \nkiedy skończysz, aby przejść dalej.

Użyj echo -nedo:

  1. nie drukuj \ni
  2. rozpoznawać sekwencje specjalne, takie jak \r.

Oto demo:

echo -ne '#####                     (33%)\r'
sleep 1
echo -ne '#############             (66%)\r'
sleep 1
echo -ne '#######################   (100%)\r'
echo -ne '\n'

W komentarzu poniżej puk wspomina, że ​​„to się nie udaje”, jeśli zaczynasz od długiej linii, a następnie chcesz napisać krótką linię: W takim przypadku musisz zastąpić długość długiej linii (np. Spacjami).

Mitch Haile
źródło
23
Zgodnie ze stroną podręcznika echa (przynajmniej w systemie MacOS X) sh / bash używają własnego wbudowanego polecenia echo, które nie akceptuje „-n” ... więc aby osiągnąć to samo, musisz umieścić \ r \ c na końcu ciągu, zamiast po prostu \ r
Justin Jenkins
51
Przenośnym sposobem na wyprowadzenie tego jest użycie printfzamiast echo.
Jens
9
w przypadku printf musielibyśmy użyć tego formatu: printf "#### (50%%)\r"nie działałoby to z pojedynczymi cudzysłowami, a znak procentu musi być poprzedzony znakiem ucieczki.
nurettin
7
Nie rozumiem tego - zaakceptowałem i mnóstwo pozytywnych opinii na temat hacka „Zgadnę, jak długo ta operacja potrwa na nieznanym sprzęcie” hack? pv jest poprawną odpowiedzią IMO (ale bar też to zrobi)
Stephen
19
Pytanie brzmiało „Jak zrobić paski postępu” na przykładzie kopiowania plików. Skoncentrowałem się na problemie z „grafiką”, a nie na obliczeniu odległości od operacji kopiowania pliku.
Mitch Haile
73

Możesz być także zainteresowany tym, jak zrobić spinner :

Czy mogę zrobić spinner w Bash?

Pewnie!

i=1
sp="/-\|"
echo -n ' '
while true
do
    printf "\b${sp:i++%${#sp}:1}"
done

Za każdym razem, gdy pętla się iteruje, wyświetla następny znak w łańcuchu sp, owijając się wokół końca. (i to pozycja bieżącego znaku do wyświetlenia, a $ {# sp} to długość łańcucha sp).

Ciąg \ b jest zastąpiony znakiem „backspace”. Możesz też zagrać za pomocą \ r, aby wrócić do początku linii.

Jeśli chcesz zwolnić, umieść polecenie uśpienia w pętli (po printf).

Odpowiednikiem POSIX byłoby:

sp='/-\|'
printf ' '
while true; do
    printf '\b%.1s' "$sp"
    sp=${sp#?}${sp%???}
done

Jeśli masz już pętlę, która wykonuje dużo pracy, możesz wywołać następującą funkcję na początku każdej iteracji, aby zaktualizować przędzarkę:

sp="/-\|"
sc=0
spin() {
   printf "\b${sp:sc++:1}"
   ((sc==${#sp})) && sc=0
}
endspin() {
   printf "\r%s\n" "$@"
}

until work_done; do
   spin
   some_work ...
done
endspin
Daenyth
źródło
15
Znacznie krótsza wersja, w pełni przenośna *: while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*: sleepmoże wymagać znaków ints, a nie miejsc po przecinku)
Adam Katz
1
@Denenyth. Dzięki. Czy powinniśmy wywoływać polecenie, które musimy obejrzeć, czyli postęp przy użyciu poprzedniego kodu?
goro
@goro: W some_work ...powyższej linii; bardziej szczegółową dyskusję opartą na tej pomocnej odpowiedzi i pomocnym komentarzu Adama Katza - z naciskiem na zgodność z POSIX - można znaleźć tutaj .
mklement0
@AdamKatz: Jest to pomocne, przenośne uproszczenie, ale aby dopasować podejście Daenyth, spinner musi opierać się na, \bzamiast \r, ponieważ w przeciwnym razie będzie działał tylko na samym początku linii: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- lub, jeśli wyświetla kursor za pokrętłem jest niepożądany:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0
1
@kaushal - Ctrl + C zatrzyma to ręcznie. Jeśli masz zadanie w tle, możesz zapisać jego PID ( job=$!), a następnie uruchomić while kill -0 $job 2>/dev/null;do …na przykład:sleep 15 & job=$!; while kill -0 $job 2>/dev/null; do for s in / - \\ \|; do printf "\r$s"; sleep .1; done; done
Adam Katz
48

Niektóre posty pokazały, jak wyświetlić postęp polecenia. Aby to obliczyć, musisz zobaczyć, ile osiągnąłeś. W systemach BSD niektóre polecenia, takie jak dd (1), akceptują SIGINFOsygnał i zgłaszają swoje postępy. W systemach Linux niektóre polecenia będą reagować podobnie SIGUSR1. Jeśli ta funkcja jest dostępna, możesz przesyłać dane wejściowe w ddcelu monitorowania liczby przetworzonych bajtów.

Alternatywnie można użyć lsofdo uzyskania przesunięcia wskaźnika odczytu pliku, a tym samym do obliczenia postępu. Napisałem polecenie o nazwie pmonitor , które wyświetla postęp przetwarzania określonego procesu lub pliku. Dzięki niemu możesz wykonywać następujące czynności:

$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%

Na moim blogu pojawia się wcześniejsza wersja skryptów powłoki Linux i FreeBSD .

Diomidis Spinellis
źródło
To jest niesamowite, ja zawsze zapominam rzeczy rur przez pv :-) Myślę, że moje polecenie „stat” działa trochę inaczej, mój (Linux) wersja tego skryptu: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b
unhammer
Świetny post, zawsze uwielbiam go, gdy awkgrasz!
ShellFish
To jest świetne! Dzięki za wspaniały skrypt!
thebeagle
47

Mam łatwą funkcję paska postępu, którą napisałem poprzedniego dnia:

#!/bin/bash
# 1. Create ProgressBar function
# 1.1 Input is currentState($1) and totalState($2)
function ProgressBar {
# Process data
    let _progress=(${1}*100/${2}*100)/100
    let _done=(${_progress}*4)/10
    let _left=40-$_done
# Build progressbar string lengths
    _fill=$(printf "%${_done}s")
    _empty=$(printf "%${_left}s")

# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:                           
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%"

}

# Variables
_start=1

# This accounts as the "totalState" variable for the ProgressBar function
_end=100

# Proof of concept
for number in $(seq ${_start} ${_end})
do
    sleep 0.1
    ProgressBar ${number} ${_end}
done
printf '\nFinished!\n'

Lub złap go,
https://github.com/fearside/ProgressBar/

strach
źródło
czy możesz wyjaśnić wiersz pod 1.2.1.1? Czy wykonujesz podstawienie sed zmiennymi _fill i _pusty? Jestem zmieszany.
Chirag,
Zamiast korzystać z sed, używam wewnętrznej bash „Substring Replacement”, ponieważ jest to łatwa praca, wolę korzystać z wewnętrznych funkcji bash do tego rodzaju pracy. Kod wygląda również ładniej. :-) Sprawdź tutaj tldp.org/LDP/abs/html/string-manipulation.html i wyszukaj zastąpienie podłańcucha.
strach
a $ {_ fill} jest przypisane jako $ {_ done} liczba spacji. To jest piękne. Świetny robotnik. Zdecydowanie użyję tego we wszystkich moich skryptach bash haha
Chirag,
Świetna robota @fearside! Zrobiłem małe poprawki, aby pominąć, gdy _progress nie zmienił się od ostatniej wartości, aby poprawić prędkość. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs
Słodkie. Zmiana kreski przez prostokąt nadaje jej bardziej profesjonalny wygląd:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI
44

użyj komendy linux pv:

http://linux.die.net/man/1/pv

nie zna rozmiaru, jeśli znajduje się w środku strumienia, ale daje prędkość i sumę, a stamtąd możesz dowiedzieć się, jak długo to potrwa i uzyskać informacje zwrotne, aby wiedzieć, że nie zawiesił się.

Seth Wegner
źródło
32

Szukałem czegoś bardziej seksownego niż wybrana odpowiedź, podobnie jak mój własny scenariusz.

Zapowiedź

progress-bar.sh w akcji

Źródło

Włożyłem to na githubprogress-bar.sh

progress-bar() {
  local duration=${1}


    already_done() { for ((done=0; done<$elapsed; done++)); do printf "▇"; done }
    remaining() { for ((remain=$elapsed; remain<$duration; remain++)); do printf " "; done }
    percentage() { printf "| %s%%" $(( (($elapsed)*100)/($duration)*100/100 )); }
    clean_line() { printf "\r"; }

  for (( elapsed=1; elapsed<=$duration; elapsed++ )); do
      already_done; remaining; percentage
      sleep 1
      clean_line
  done
  clean_line
}

Stosowanie

 progress-bar 100
Édouard Lopez
źródło
1
Nie rozumiem, w jaki sposób jest to zintegrowane z niektórymi procesami, w których długość procesu nie jest znana. Jak zatrzymać pasek postępu, jeśli mój proces zakończył się wcześniej, np. W celu rozpakowania pliku.
sty
Myślę, że użycie powinno byćprogress-bar 100
jirarium
Rzeczywiście atrakcyjny postęp. Jak można go powiązać z funkcją, która przetwarza długotrwałe działanie na zdalnych serwerach przez ssh? Mam na myśli, jak można zmierzyć czas aktualizacji (na przykład) na zdalnych serwerach?
bez twarzy
1
@faceless nie jest objęty zakresem tego kodu, podajesz czas i odlicza
Édouard Lopez,
1
@Fusion to znak Unicode (U + 2587 LOWER SEVEN EIGHTHS BLOCK), który powinien być bezpieczny dla nowoczesnej powłoki. Spróbuj swoich envs
Édouard Lopez
18

GNU tar ma przydatną opcję, która daje funkcjonalność prostego paska postępu.

(...) Kolejną dostępną akcją punktu kontrolnego jest „kropka” (lub „.”). Poleca tarowi wydrukowanie pojedynczej kropki na standardowym strumieniu listowania, np .:

$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...

Ten sam efekt można uzyskać poprzez:

$ tar -c --checkpoint=.1000 /var
Wojtek
źródło
+1 za najprostsze podejście! Jeśli nie widać wydrukowanych kropek, spróbuj na przykład zmniejszyć liczbę --checkpoint=.10. Działa również świetnie podczas ekstrakcji tar -xz.
Noam Manos
13

Prostsza metoda, która działa w moim systemie za pomocą narzędzia pipeview (pv).

srcdir=$1
outfile=$2


tar -Ocf - $srcdir | pv -i 1 -w 50 -berps `du -bs $srcdir | awk '{print $1}'` | 7za a -si $outfile
Leebert
źródło
13

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ć $iwartość, tj. Jaka część paska postępu ma zostać wyświetlona. W powyższym przykładzie po prostu pozwoliłem inkrementacji w forpętli, aby zilustrować zasadę, ale aplikacja z prawdziwego życia użyłaby nieskończonej pętli i obliczyłaby $izmienną przy każdej iteracji. Do wykonania wspomnianych obliczeń potrzebne są następujące składniki:

  1. ile pracy trzeba zrobić
  2. ile pracy wykonano do tej pory

W razie cppotrzeby 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ż $BARdynamikę, 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ę.

cprn
źródło
1
Dla tych, którzy chcą najprostszych rzeczy, stworzyłem je z pierwszą odpowiedzią cprn. To bardzo prosty pasek postępu w funkcji, która używa jakiejś głupiej reguły proporcjonalności, aby narysować pasek: pastebin.com/9imhRLYX
YCN-
9

Pasek postępu w stylu APT (nie psuje normalnej wydajności)

wprowadź opis zdjęcia tutaj

EDYCJA: Aby uzyskać zaktualizowaną wersję, sprawdź moją stronę github

Odpowiedzi na to pytanie nie były satysfakcjonujące. To, czego osobiście szukałem, to fantazyjny pasek postępu, jak widzi APT.

Rzuciłem okiem na kod źródłowy C dla APT i postanowiłem napisać własny odpowiednik dla bash.

Pasek postępu pozostanie ładnie na dole terminala i nie będzie zakłócał żadnego wyjścia wysyłanego do terminala.

Należy pamiętać, że pasek jest obecnie ustawiony na szerokość 100 znaków. Jeśli chcesz przeskalować go do rozmiaru terminala, jest to również dość łatwe do osiągnięcia (zaktualizowana wersja na mojej stronie github dobrze sobie z tym radzi).

Tutaj opublikuję mój skrypt. Przykład użycia:

source ./progress_bar.sh
echo "This is some output"
setup_scroll_area
sleep 1
echo "This is some output 2"
draw_progress_bar 10
sleep 1
echo "This is some output 3"
draw_progress_bar 50
sleep 1
echo "This is some output 4"
draw_progress_bar 90
sleep 1
echo "This is some output 5"
destroy_scroll_area

Skrypt (zdecydowanie zalecam wersję na moim githubie):

#!/bin/bash

# This code was inspired by the open source C code of the APT progress bar
# http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/trusty/apt/trusty/view/head:/apt-pkg/install-progress.cc#L233

#
# Usage:
# Source this script
# setup_scroll_area
# draw_progress_bar 10
# draw_progress_bar 90
# destroy_scroll_area
#


CODE_SAVE_CURSOR="\033[s"
CODE_RESTORE_CURSOR="\033[u"
CODE_CURSOR_IN_SCROLL_AREA="\033[1A"
COLOR_FG="\e[30m"
COLOR_BG="\e[42m"
RESTORE_FG="\e[39m"
RESTORE_BG="\e[49m"

function setup_scroll_area() {
    lines=$(tput lines)
    let lines=$lines-1
    # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row
    echo -en "\n"

    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # Start empty progress bar
    draw_progress_bar 0
}

function destroy_scroll_area() {
    lines=$(tput lines)
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"
    # Set scroll region (this will place the cursor in the top left)
    echo -en "\033[0;${lines}r"

    # Restore cursor but ensure its inside the scrolling area
    echo -en "$CODE_RESTORE_CURSOR"
    echo -en "$CODE_CURSOR_IN_SCROLL_AREA"

    # We are done so clear the scroll bar
    clear_progress_bar

    # Scroll down a bit to avoid visual glitch when the screen area grows by one row
    echo -en "\n\n"
}

function draw_progress_bar() {
    percentage=$1
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # Clear progress bar
    tput el

    # Draw progress bar
    print_bar_text $percentage

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

function clear_progress_bar() {
    lines=$(tput lines)
    let lines=$lines
    # Save cursor
    echo -en "$CODE_SAVE_CURSOR"

    # Move cursor position to last row
    echo -en "\033[${lines};0f"

    # clear progress bar
    tput el

    # Restore cursor position
    echo -en "$CODE_RESTORE_CURSOR"
}

function print_bar_text() {
    local percentage=$1

    # Prepare progress bar
    let remainder=100-$percentage
    progress_bar=$(echo -ne "["; echo -en "${COLOR_FG}${COLOR_BG}"; printf_new "#" $percentage; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "." $remainder; echo -ne "]");

    # Print progress bar
    if [ $1 -gt 99 ]
    then
        echo -ne "${progress_bar}"
    else
        echo -ne "${progress_bar}"
    fi
}

printf_new() {
    str=$1
    num=$2
    v=$(printf "%-${num}s" "$str")
    echo -ne "${v// /$str}"
}
polle
źródło
7

Umożliwia to wizualizację, że polecenie jest nadal wykonywane:

while :;do echo -n .;sleep 1;done &
trap "kill $!" EXIT  #Die with parent if we die prematurely
tar zxf packages.tar.gz; # or any other command here
kill $! && trap " " EXIT #Kill the loop and unset the trap or else the pid might get reassigned and we might end up killing a completely different process

Spowoduje to utworzenie nieskończonej pętli while, która będzie działać w tle i wywoła echo „.” każda sekunda. To wyświetli się .w powłoce. Uruchom tarpolecenie lub dowolne polecenie. Po zakończeniu wykonywania tego polecenia zabij ostatnie zadanie działające w tle - czyli nieskończoną pętlę while .

romeror
źródło
Czy inne zadanie nie mogło rozpocząć się w tle podczas wykonywania i potencjalnie zostać zabite zamiast pętli postępu?
Centimane
Myślę, że pomysł polega na tym, że umieściłbyś to w skrypcie, więc zatrzymałoby to jedynie wyjście z tego skryptu.
Iguananaut
1
Uwielbiam to polecenie, używam go w moich plikach. Jestem trochę niespokojny, ponieważ tak naprawdę nie rozumiem, jak to działa. Pierwszy i trzeci wiersz są łatwiejsze do zrozumienia, ale nadal nie jestem pewien. Wiem, że to stara odpowiedź, ale czy istnieje sposób, aby uzyskać inne wyjaśnienie dla początkujących programistów
Felipe
1
Jest to JEDYNA prawdziwa odpowiedź, gdzie inni to tylko paski postępu zabawek w Skrypcie 101, które nic nie znaczą i nie nadają się do prawdziwych, jednorazowych, niemożliwych do śledzenia (prawie WSZYSTKICH) programów. Dziękuję Ci.
bekce
@Felipe, Pętla while jest procesem w tle. $! w pierwszej pułapce przechwytuje identyfikator procesu w tle i zapewnia, że ​​jeśli proces bieżący / macierzysty zakończy się, proces w tle również umiera i nie pozostaje zawieszony. Instrukcja kill kończy proces w tle, gdy kończy się długie polecenie lub polecenie.
floydn
7

Oto jak może to wyglądać

Przesyłanie pliku

[##################################################] 100% (137921 / 137921 bytes)

Oczekiwanie na zakończenie zadania

[#########################                         ] 50% (15 / 30 seconds)

Prosta funkcja, która to implementuje

Możesz po prostu skopiować i wkleić go do skryptu. Nie wymaga niczego innego do działania.

PROGRESS_BAR_WIDTH=50  # progress bar length in characters

draw_progress_bar() {
  # Arguments: current value, max value, unit of measurement (optional)
  local __value=$1
  local __max=$2
  local __unit=${3:-""}  # if unit is not supplied, do not display it

  # Calculate percentage
  if (( $__max < 1 )); then __max=1; fi  # anti zero division protection
  local __percentage=$(( 100 - ($__max*100 - $__value*100) / $__max ))

  # Rescale the bar according to the progress bar width
  local __num_bar=$(( $__percentage * $PROGRESS_BAR_WIDTH / 100 ))

  # Draw progress bar
  printf "["
  for b in $(seq 1 $__num_bar); do printf "#"; done
  for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH - $__num_bar ))); do printf " "; done
  printf "] $__percentage%% ($__value / $__max $__unit)\r"
}

Przykład użycia

Tutaj przesyłamy plik i przerysowujemy pasek postępu przy każdej iteracji. Nie ma znaczenia, jakie zadanie jest faktycznie wykonywane, o ile możemy uzyskać 2 wartości: wartość maksymalną i wartość bieżącą.

W poniższym przykładzie maksymalna wartość to, file_sizea bieżąca wartość jest dostarczana przez jakąś funkcję i jest wywoływana uploaded_bytes.

# Uploading a file
file_size=137921

while true; do
  # Get current value of uploaded bytes
  uploaded_bytes=$(some_function_that_reports_progress)

  # Draw a progress bar
  draw_progress_bar $uploaded_bytes $file_size "bytes"

  # Check if we reached 100%
  if [ $uploaded_bytes == $file_size ]; then break; fi
  sleep 1  # Wait before redrawing
done
# Go to the newline at the end of upload
printf "\n"
Vagiz Duseev
źródło
Schludna i prosta funkcja. Wielkie dzięki!
Andreas Kraft
Właśnie tego szukam!
Wielkie
4

Większość poleceń uniksowych nie daje ci takiej bezpośredniej informacji zwrotnej, z której możesz to zrobić. Niektóre dadzą ci wyjście na stdout lub stderr, których możesz użyć.

W przypadku czegoś takiego jak tar można użyć przełącznika -v i potokować dane wyjściowe do programu, który aktualizuje małą animację dla każdego czytanego wiersza. Gdy tar wypisuje listę plików, jest to rozwikłane, program może zaktualizować animację. Aby wykonać procent ukończenia, musisz znać liczbę plików i policzyć wiersze.

cp nie daje tego rodzaju danych wyjściowych, o ile mi wiadomo. Aby monitorować postęp cp, musisz monitorować pliki źródłowe i docelowe oraz obserwować rozmiar miejsca docelowego. Możesz napisać mały program c przy użyciu wywołania systemowego stat (2) , aby uzyskać rozmiar pliku. Odczytuje to rozmiar źródła, a następnie sonduje plik docelowy i aktualizuje pasek% complete na podstawie rozmiaru zapisanego do tej pory pliku.

ConcernedOfTunbridgeWells
źródło
4

Moje rozwiązanie wyświetla procent tarballa, który jest obecnie rozpakowywany i zapisywany. Używam tego podczas zapisywania obrazów głównego systemu plików o wielkości 2 GB. Naprawdę potrzebujesz paska postępu dla tych rzeczy. To, co robię, służy gzip --listdo uzyskania całkowitego nieskompresowanego rozmiaru archiwum. Na tej podstawie obliczam współczynnik blokowania potrzebny do podzielenia pliku na 100 części. Na koniec drukuję komunikat punktu kontrolnego dla każdego bloku. W przypadku pliku 2 GB daje to około 10 MB bloku. Jeśli jest to zbyt duże, możesz podzielić BLOCKING_FACTOR przez 10 lub 100, ale wtedy trudniej jest wydrukować ładne wyniki w procentach.

Zakładając, że używasz Bash, możesz użyć następującej funkcji powłoki

untar_progress () 
{ 
  TARBALL=$1
  BLOCKING_FACTOR=$(gzip --list ${TARBALL} |
    perl -MPOSIX -ane '$.==2 && print ceil $F[1]/50688')
  tar --blocking-factor=${BLOCKING_FACTOR} --checkpoint=1 \
    --checkpoint-action='ttyout=Wrote %u%  \r' -zxf ${TARBALL}
}
Noah Spurrier
źródło
Fajne rozwiązanie, ale co zrobić, gdy chcesz skompresować katalog?
Samir Sadek
4

Po pierwsze, pasek nie jest jedynym miernikiem postępu rur. Drugi (może nawet bardziej znany) to pv (przeglądarka potoków).

Po drugie, bar i pv mogą być używane na przykład w następujący sposób:

$ bar file1 | wc -l 
$ pv file1 | wc -l

lub nawet:

$ tail -n 100 file1 | bar | wc -l
$ tail -n 100 file1 | pv | wc -l

jedną przydatną sztuczką, jeśli chcesz użyć bar i pv w komendach, które działają z plikami podanymi w argumentach, np. kopiuj plik1 plik2, jest użycie podstawienia procesu :

$ copy <(bar file1) file2
$ copy <(pv file1) file2

Podstawianie procesów to bashowa magia, która tworzy tymczasowe pliki potoków FIFO / dev / fd / i łączy stdout z uruchomionego procesu (w nawiasach) przez ten potok i kopiuje traktuje go jak zwykły plik (z jednym wyjątkiem, może go tylko odczytać do przodu).

Aktualizacja:

samo polecenie bar pozwala również na kopiowanie. Po pasku man:

bar --in-file /dev/rmt/1cbn --out-file \
     tape-restore.tar --size 2.4g --buffer-size 64k

Ale moim zdaniem podstawianie procesów jest bardziej ogólne. Używa samego programu cp.

thedk
źródło
3

Wolę używać okna dialogowego z parametrem --gauge . Jest bardzo często używany w instalacjach pakietów .deb i innych podstawowych elementach konfiguracyjnych wielu dystrybucji. Więc nie musisz wymyślać koła ponownie ... ponownie

Wystarczy podać wartość int od 1 do 100 @stdin. Jeden podstawowy i głupi przykład:

for a in {1..100}; do sleep .1s; echo $a| dialog --gauge "waiting" 7 30; done

Mam ten plik / bin / Wait (z chmod u + x perms) do gotowania: P

#!/bin/bash
INIT=`/bin/date +%s`
NOW=$INIT
FUTURE=`/bin/date -d "$1" +%s`
[ $FUTURE -a $FUTURE -eq $FUTURE ] || exit
DIFF=`echo "$FUTURE - $INIT"|bc -l`

while [ $INIT -le $FUTURE -a $NOW -lt $FUTURE ]; do
    NOW=`/bin/date +%s`
    STEP=`echo "$NOW - $INIT"|bc -l`
    SLEFT=`echo "$FUTURE - $NOW"|bc -l`
    MLEFT=`echo "scale=2;$SLEFT/60"|bc -l`
    TEXT="$SLEFT seconds left ($MLEFT minutes)";
    TITLE="Waiting $1: $2"
    sleep 1s
    PTG=`echo "scale=0;$STEP * 100 / $DIFF"|bc -l`
    echo $PTG| dialog --title "$TITLE" --gauge "$TEXT" 7 72
done

if [ "$2" == "" ]; then msg="Espera terminada: $1";audio="Listo";
else msg=$2;audio=$2;fi 

/usr/bin/notify-send --icon=stock_appointment-reminder-excl "$msg"
espeak -v spanish "$audio"

Mogę więc umieścić:

Wait "34 min" "warm up the oven"

lub

Wait "dec 31" "happy new year"

Juan Eduardo Castaño Nestares
źródło
2

dla mnie najłatwiejszym w użyciu i jak dotąd najlepiej wyglądającym jest polecenie pvlub barjak jakiś facet już napisał

na przykład: trzeba wykonać kopię zapasową całego dysku za pomocą dd

zwykle używasz dd if="$input_drive_path" of="$output_file_path"

z pvtobą możesz to zrobić tak:

dd if="$input_drive_path" | pv | dd of="$output_file_path"

a postęp idzie bezpośrednio do STDOUTtego:

    7.46GB 0:33:40 [3.78MB/s] [  <=>                                            ]

po zakończeniu pojawi się podsumowanie

    15654912+0 records in
    15654912+0 records out
    8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s
lukassos
źródło
Czy możesz używać pvlub barwizualizować postęp różnych procesów, np. Odliczanie czasu, pozycję w pliku tekstowym, instalację aplikacji, konfigurację środowiska uruchomieniowego itp.?
cprn
2

Wiele odpowiedzi opisuje pisanie własnych poleceń do wydrukowania '\r' + $some_sort_of_progress_msg. Problem czasami polega na tym, że wydrukowanie setek takich aktualizacji na sekundę spowolni proces.

Jeśli jednak któryś z twoich procesów generuje dane wyjściowe (np. Wyświetli 7z a -r newZipFile myFolderkażdą nazwę pliku podczas kompresji), istnieje prostsze, szybkie, bezbolesne i konfigurowalne rozwiązanie.

Zainstaluj moduł python tqdm.

$ sudo pip install tqdm
$ # now have fun
$ 7z a -r -bd newZipFile myFolder | tqdm >> /dev/null
$ # if we know the expected total, we can have a bar!
$ 7z a -r -bd newZipFile myFolder | grep -o Compressing | tqdm --total $(find myFolder -type f | wc -l) >> /dev/null

Pomoc: tqdm -h. Przykład z większą liczbą opcji:

$ find / -name '*.py' -exec cat \{} \; | tqdm --unit loc --unit_scale True | wc -l

Jako bonus można także wykorzystać tqdmdo zawijania iterable w kodzie Pythona.

https://github.com/tqdm/tqdm/blob/master/README.rst#module

casper.dcl
źródło
Nie sądzę, żeby twój przykład z „więcej opcji” działał. Wydaje się, że przekazuje tqdmSTDOUT wc -lprzez rurę. Prawdopodobnie chcesz od tego uciec.
cprn
1
@ cprn tqdmpokaże postęp STDERRpodczas przesyłania danych wejściowych STDINdo STDOUT. W takim przypadku wc -lotrzymalibyśmy takie same dane wejściowe, jakby tqdmnie zostały uwzględnione.
casper.dcl
Ach, ma to teraz sens. Dziękuję za wyjaśnienie.
cprn
2

Na podstawie pracy Edouarda Lopeza stworzyłem pasek postępu, który pasuje do wielkości ekranu, cokolwiek to jest. Sprawdź to.

wprowadź opis zdjęcia tutaj

Jest także opublikowany w Git Hub .

#!/bin/bash
#
# Progress bar by Adriano Pinaffo
# Available at https://github.com/adriano-pinaffo/progressbar.sh
# Inspired on work by Edouard Lopez (https://github.com/edouard-lopez/progress-bar.sh)
# Version 1.0
# Date April, 28th 2017

function error {
  echo "Usage: $0 [SECONDS]"
  case $1 in
    1) echo "Pass one argument only"
    exit 1
    ;;
    2) echo "Parameter must be a number"
    exit 2
    ;;
    *) echo "Unknown error"
    exit 999
  esac
}

[[ $# -ne 1 ]] && error 1
[[ $1 =~ ^[0-9]+$ ]] || error 2

duration=${1}
barsize=$((`tput cols` - 7))
unity=$(($barsize / $duration))
increment=$(($barsize%$duration))
skip=$(($duration/($duration-$increment)))
curr_bar=0
prev_bar=
for (( elapsed=1; elapsed<=$duration; elapsed++ ))
do
  # Elapsed
prev_bar=$curr_bar
  let curr_bar+=$unity
  [[ $increment -eq 0 ]] || {  
    [[ $skip -eq 1 ]] &&
      { [[ $(($elapsed%($duration/$increment))) -eq 0 ]] && let curr_bar++; } ||
    { [[ $(($elapsed%$skip)) -ne 0 ]] && let curr_bar++; }
  }
  [[ $elapsed -eq 1 && $increment -eq 1 && $skip -ne 1 ]] && let curr_bar++
  [[ $(($barsize-$curr_bar)) -eq 1 ]] && let curr_bar++
  [[ $curr_bar -lt $barsize ]] || curr_bar=$barsize
  for (( filled=0; filled<=$curr_bar; filled++ )); do
    printf "▇"
  done

  # Remaining
  for (( remain=$curr_bar; remain<$barsize; remain++ )); do
    printf " "
  done

  # Percentage
  printf "| %s%%" $(( ($elapsed*100)/$duration))

  # Return
  sleep 1
  printf "\r"
done
printf "\n"
exit 0

Cieszyć się

Adriano_Pinaffo
źródło
1

Ma to zastosowanie tylko przy użyciu zenity gnome. Zenity zapewnia świetny natywny interfejs do skryptów bash: https://help.gnome.org/users/zenity/stable/

Z paska postępu Zenity Przykład:

#!/bin/sh
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "20" ; sleep 1
echo "# Resetting cron jobs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "75" ; sleep 1
echo "# Rebooting system" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
  --title="Update System Logs" \
  --text="Scanning mail logs..." \
  --percentage=0

if [ "$?" = -1 ] ; then
        zenity --error \
          --text="Update canceled."
fi
tPSU
źródło
1

Użyłem odpowiedzi z sekcji Tworzenie ciągu powtarzających się znaków w skrypcie powłoki do powtarzania znaków . Mam dwie stosunkowo małe wersje bash dla skryptów, które muszą wyświetlać pasek postępu (na przykład pętla, która przechodzi przez wiele plików, ale nie jest przydatna w przypadku dużych plików tar lub operacji kopiowania). Szybsza składa się z dwóch funkcji, z których jedna służy do przygotowania ciągów do wyświetlania słupkowego:

preparebar() {
# $1 - bar length
# $2 - bar char
    barlen=$1
    barspaces=$(printf "%*s" "$1")
    barchars=$(printf "%*s" "$1" | tr ' ' "$2")
}

i jeden, aby wyświetlić pasek postępu:

progressbar() {
# $1 - number (-1 for clearing the bar)
# $2 - max number
    if [ $1 -eq -1 ]; then
        printf "\r  $barspaces\r"
    else
        barch=$(($1*barlen/$2))
        barsp=$((barlen-barch))
        printf "\r[%.${barch}s%.${barsp}s]\r" "$barchars" "$barspaces"
    fi
}

Może być używany jako:

preparebar 50 "#"

co oznacza przygotowanie ciągów dla paska ze 50 znakami „#”, a następnie:

progressbar 35 80

wyświetli liczbę znaków „#” odpowiadającą proporcji 35/80:

[#####################                             ]

Należy pamiętać, że funkcja wyświetla pasek w kółko w tej samej linii, dopóki ty (lub inny program) nie wydrukujesz nowej linii. Jeśli jako pierwszy parametr podasz -1, pasek zostanie usunięty:

progressbar -1 80

Wolniejsza wersja ma jedną funkcję:

progressbar() {
# $1 - number
# $2 - max number
# $3 - number of '#' characters
    if [ $1 -eq -1 ]; then
        printf "\r  %*s\r" "$3"
    else
        i=$(($1*$3/$2))
        j=$(($3-i))
        printf "\r[%*s" "$i" | tr ' ' '#'
        printf "%*s]\r" "$j"
    fi
}

i może być użyty jako (ten sam przykład jak powyżej):

progressbar 35 80 50

Jeśli potrzebujesz paska postępu na stderr, po prostu dodaj >&2na końcu każdego polecenia printf.

Zarko Zivanov
źródło
1

Aby wskazać postęp aktywności, wypróbuj następujące polecenia:

while true; do sleep 0.25 && echo -ne "\r\\" && sleep 0.25 && echo -ne "\r|" && sleep 0.25 && echo -ne "\r/" && sleep 0.25 && echo -ne "\r-"; done;

LUB

while true; do sleep 0.25 && echo -ne "\rActivity: \\" && sleep 0.25 && echo -ne "\rActivity: |" && sleep 0.25 && echo -ne "\rActivity: /" && sleep 0.25 && echo -ne "\rActivity: -"; done;

LUB

while true; do sleep 0.25 && echo -ne "\r" && sleep 0.25 && echo -ne "\r>" && sleep 0.25 && echo -ne "\r>>" && sleep 0.25 && echo -ne "\r>>>"; sleep 0.25 && echo -ne "\r>>>>"; done;

LUB

while true; do sleep .25 && echo -ne "\r:Active:" && sleep .25 && echo -ne "\r:aCtive:" && sleep .25 && echo -ne "\r:acTive:" && sleep .25 && echo -ne "\r:actIve:" && sleep .25 && echo -ne "\r:actiVe:" && sleep .25 && echo -ne "\r:activE:"; done;

Można użyć flag / zmiennych wewnątrz pętli while, aby sprawdzić i wyświetlić wartość / zakres postępu.

S471
źródło
1

Korzystając z wyżej wymienionych sugestii, zdecydowałem się zaimplementować własny pasek postępu.

#!/usr/bin/env bash

main() {
  for (( i = 0; i <= 100; i=$i + 1)); do
    progress_bar "$i"
    sleep 0.1;
  done
  progress_bar "done"
  exit 0
}

progress_bar() {
  if [ "$1" == "done" ]; then
    spinner="X"
    percent_done="100"
    progress_message="Done!"
    new_line="\n"
  else
    spinner='/-\|'
    percent_done="${1:-0}"
    progress_message="$percent_done %"
  fi

  percent_none="$(( 100 - $percent_done ))"
  [ "$percent_done" -gt 0 ] && local done_bar="$(printf '#%.0s' $(seq -s ' ' 1 $percent_done))"
  [ "$percent_none" -gt 0 ] && local none_bar="$(printf '~%.0s' $(seq -s ' ' 1 $percent_none))"

  # print the progress bar to the screen
  printf "\r Progress: [%s%s] %s %s${new_line}" \
    "$done_bar" \
    "$none_bar" \
    "${spinner:x++%${#spinner}:1}" \
    "$progress_message"
}

main "$@"
Qub3r
źródło
1
Miły! aby to działało, musiałem zmienić linię percent_none="$(( 100 - "$percent_done" ))"napercent_none="$(( 100 - $percent_done))"
sergio
0

Zrobiłem czystą wersję powłoki dla systemu osadzonego, wykorzystując:

  • Funkcja obsługi sygnału SIGUSR1 w / usr / bin / dd.

    Zasadniczo, jeśli wyślesz polecenie „kill SIGUSR1 $ (pid_of_running_dd_process)”, wyświetli ono podsumowanie prędkości transmisji i przesłanej kwoty.

  • uruchamianie w tle dd, a następnie regularne sprawdzanie go w poszukiwaniu aktualizacji i generowanie znaczników skrótu, takich jak dawni klienci ftp.

  • Używanie / dev / stdout jako miejsca docelowego dla programów sprzyjających innym standardom, takich jak scp

Wynik końcowy pozwala wykonać dowolną operację przesyłania plików i uzyskać aktualizację postępu, która wygląda jak dane wyjściowe „haszowania” starej szkoły FTP, w których po prostu uzyskasz znak krzyżyka dla każdego X bajtów.

To nie jest kod jakości produkcji, ale masz pomysł. Myślę, że to słodkie.

Rzeczywista liczba bajtów może być niepoprawnie odzwierciedlona w liczbie skrótów - w zależności od problemów z zaokrąglaniem może być ich więcej lub mniej. Nie używaj tego jako części skryptu testowego, to po prostu cukierki. I tak, wiem, że jest to strasznie nieefektywne - to skrypt powłoki i nie przepraszam za to.

Przykłady z wget, scp i tftp na końcu. Powinien działać ze wszystkim, co emituje dane. Upewnij się, że używasz / dev / stdout dla programów, które nie są przyjazne stdout.

#!/bin/sh
#
# Copyright (C) Nathan Ramella ([email protected]) 2010 
# LGPLv2 license
# If you use this, send me an email to say thanks and let me know what your product
# is so I can tell all my friends I'm a big man on the internet!

progress_filter() {

        local START=$(date +"%s")
        local SIZE=1
        local DURATION=1
        local BLKSZ=51200
        local TMPFILE=/tmp/tmpfile
        local PROGRESS=/tmp/tftp.progress
        local BYTES_LAST_CYCLE=0
        local BYTES_THIS_CYCLE=0

        rm -f ${PROGRESS}

        dd bs=$BLKSZ of=${TMPFILE} 2>&1 \
                | grep --line-buffered -E '[[:digit:]]* bytes' \
                | awk '{ print $1 }' >> ${PROGRESS} &

        # Loop while the 'dd' exists. It would be 'more better' if we
        # actually looked for the specific child ID of the running 
        # process by identifying which child process it was. If someone
        # else is running dd, it will mess things up.

        # My PID handling is dumb, it assumes you only have one running dd on
        # the system, this should be fixed to just get the PID of the child
        # process from the shell.

        while [ $(pidof dd) -gt 1 ]; do

                # PROTIP: You can sleep partial seconds (at least on linux)
                sleep .5    

                # Force dd to update us on it's progress (which gets
                # redirected to $PROGRESS file.
                # 
                # dumb pid handling again
                pkill -USR1 dd

                local BYTES_THIS_CYCLE=$(tail -1 $PROGRESS)
                local XFER_BLKS=$(((BYTES_THIS_CYCLE-BYTES_LAST_CYCLE)/BLKSZ))

                # Don't print anything unless we've got 1 block or more.
                # This allows for stdin/stderr interactions to occur
                # without printing a hash erroneously.

                # Also makes it possible for you to background 'scp',
                # but still use the /dev/stdout trick _even_ if scp
                # (inevitably) asks for a password. 
                #
                # Fancy!

                if [ $XFER_BLKS -gt 0 ]; then
                        printf "#%0.s" $(seq 0 $XFER_BLKS)
                        BYTES_LAST_CYCLE=$BYTES_THIS_CYCLE
                fi
        done

        local SIZE=$(stat -c"%s" $TMPFILE)
        local NOW=$(date +"%s")

        if [ $NOW -eq 0 ]; then
                NOW=1
        fi

        local DURATION=$(($NOW-$START))
        local BYTES_PER_SECOND=$(( SIZE / DURATION ))
        local KBPS=$((SIZE/DURATION/1024))
        local MD5=$(md5sum $TMPFILE | awk '{ print $1 }')

        # This function prints out ugly stuff suitable for eval() 
        # rather than a pretty string. This makes it a bit more 
        # flexible if you have a custom format (or dare I say, locale?)

        printf "\nDURATION=%d\nBYTES=%d\nKBPS=%f\nMD5=%s\n" \
            $DURATION \
            $SIZE \
            $KBPS \
            $MD5
}

Przykłady:

echo "wget"
wget -q -O /dev/stdout http://www.blah.com/somefile.zip | progress_filter

echo "tftp"
tftp -l /dev/stdout -g -r something/firmware.bin 192.168.1.1 | progress_filter

echo "scp"
scp user@192.168.1.1:~/myfile.tar /dev/stdout | progress_filter
synthesizerpatel
źródło
Przyzwoity pomysł, o ile masz wcześniej rozmiar pliku, możesz w ten sposób zapewnić wartość dodaną niż pv, ale ślepe zasygnalizowanie pidof ddjest przerażające.
Próbowałem to nazwać słowem „# Moja obsługa PID jest głupia”
synthesizerpatel,
Możesz być może przechwycić $!od ddi czekać na [[ -e /proc/${DD_PID} ]].
0

Jeśli musisz pokazać pasek postępu czasowego (znając wcześniej czas wyświetlania), możesz użyć Pythona w następujący sposób:

#!/bin/python
from time import sleep
import sys

if len(sys.argv) != 3:
    print "Usage:", sys.argv[0], "<total_time>", "<progressbar_size>"
    exit()

TOTTIME=float(sys.argv[1])
BARSIZE=float(sys.argv[2])

PERCRATE=100.0/TOTTIME
BARRATE=BARSIZE/TOTTIME

for i in range(int(TOTTIME)+1):
    sys.stdout.write('\r')
    s = "[%-"+str(int(BARSIZE))+"s] %d%% "
    sys.stdout.write(s % ('='*int(BARRATE*i), int(PERCRATE*i)))
    sys.stdout.flush()
    SLEEPTIME = 1.0
    if i == int(TOTTIME): SLEEPTIME = 0.1
    sleep(SLEEPTIME)
print ""

Następnie, zakładając, że zapisałeś skrypt Python jako progressbar.py, możliwe jest wyświetlenie paska postępu ze skryptu bash, uruchamiając następującą komendę:

python progressbar.py 10 50

Pokazuje 50znaki wielkości paska postępu i „biegnie” przez 10kilka sekund.

auino
źródło
0

Oparłem się na odpowiedzi udzielonej przez strach

Łączy się to z bazą danych Oracle w celu pobrania postępu przywracania RMAN.

#!/bin/bash

 # 1. Create ProgressBar function
 # 1.1 Input is currentState($1) and totalState($2)
 function ProgressBar {
 # Process data
let _progress=(${1}*100/${2}*100)/100
let _done=(${_progress}*4)/10
let _left=40-$_done
# Build progressbar string lengths
_fill=$(printf "%${_done}s")
_empty=$(printf "%${_left}s")

# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%"

}

function rman_check {
sqlplus -s / as sysdba <<EOF
set heading off
set feedback off
select
round((sofar/totalwork) * 100,0) pct_done
from
v\$session_longops
where
totalwork > sofar
AND
opname NOT LIKE '%aggregate%'
AND
opname like 'RMAN%';
exit
EOF
}

# Variables
_start=1

# This accounts as the "totalState" variable for the ProgressBar function
_end=100

_rman_progress=$(rman_check)
#echo ${_rman_progress}

# Proof of concept
#for number in $(seq ${_start} ${_end})

while [ ${_rman_progress} -lt 100 ]
do

for number in _rman_progress
do
sleep 10
ProgressBar ${number} ${_end}
done

_rman_progress=$(rman_check)

done
printf '\nFinished!\n'
CH55
źródło
0
#!/bin/bash

function progress_bar() {
    bar=""
    total=10
    [[ -z $1 ]] && input=0 || input=${1}
    x="##"
   for i in `seq 1 10`; do
        if [ $i -le $input ] ;then
            bar=$bar$x
        else
            bar="$bar  "
       fi
    done
    #pct=$((200*$input/$total % 2 + 100*$input/$total))
    pct=$(($input*10))
    echo -ne "Progress : [ ${bar} ] (${pct}%) \r"    
    sleep 1
    if [ $input -eq 10 ] ;then
        echo -ne '\n'
    fi

}

mógłby stworzyć funkcję, która rysuje to w skali, powiedzmy 1-10 dla liczby pasków:

progress_bar 1
echo "doing something ..."
progress_bar 2
echo "doing something ..."
progress_bar 3
echo "doing something ..."
progress_bar 8
echo "doing something ..."
progress_bar 10
Mike Q
źródło
0
#!/bin/bash
tot=$(wc -c /proc/$$/fd/255 | awk '/ /{print $1}')
now() {
echo $(( 100* ($(awk '/^pos:/{print $2}' < /proc/$$/fdinfo/255)-166) / (tot-166) )) "%"
}
now;
now;
now;
now;
now;
now;
now;
now;
now;

wynik:

0 %
12 %
25 %
37 %
50 %
62 %
75 %
87 %
100 %

Uwaga: jeśli zamiast 255 wstawisz 1, będziesz monitorować standard w ... z 2 standardem na wyjściu (ale musisz zmodyfikować źródło, aby ustawić „tot” na wielkość wyświetlanego pliku wyjściowego)

Zibri
źródło