Come aggiungere una barra di avanzamento a uno script di shell?


413

Quando si esegue lo scripting in bash o qualsiasi altra shell in * NIX, mentre si esegue un comando che richiederà più di qualche secondo, è necessaria una barra di avanzamento.

Ad esempio, copiando un file di grandi dimensioni, aprendo un file tar di grandi dimensioni.

Quali modi consigliate di aggiungere barre di avanzamento agli script di shell?


Vedere anche stackoverflow.com/questions/12498304/… per esempi della logica di controllo (eseguire il background di un lavoro e fare qualcosa fino al termine).
Tripleee,

1
C'è una serie di requisiti che troviamo spesso utili durante lo scripting. registrazione, visualizzazione di progressi, colori, output elaborati ecc ... Ho sempre pensato che ci dovrebbe essere una sorta di semplice framework di scripting. Alla fine ho deciso di implementarne uno poiché non ne ho trovato nessuno. Potresti trovare questo utile. È in puro bash, intendo solo Bash. github.com/SumuduLansakara/JustBash
Sumudu

Non dovrebbe essere spostato su unix.stackexchange.com ?
Ethan,

Risposte:


685

Puoi implementarlo sovrascrivendo una riga. Utilizzare \rper tornare all'inizio della riga senza scrivere \nsul terminale.

Scrivi \nquando hai finito per far avanzare la linea.

Utilizzare echo -neper:

  1. non stampare \ne
  2. riconoscere sequenze di escape come \r.

Ecco una demo:

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

In un commento qui sotto, puk menziona questo "fallimento" se inizi con una linea lunga e poi vuoi scrivere una linea corta: In questo caso, dovrai sovrascrivere la lunghezza della linea lunga (ad esempio, con spazi).


23
Secondo la pagina man di echo (almeno su MacOS X) sh / bash usa il proprio comando echo incorporato che non accetta "-n" ... quindi per realizzare la stessa cosa devi mettere \ r \ c alla fine della stringa, invece di \ r
Justin Jenkins

51
Il modo portatile per produrre questo è usare printfinvece di echo.
Jens,

9
per printf dovremmo usare questo formato: printf "#### (50%%)\r"non funzionerebbe con virgolette singole e il segno di percentuale deve essere evitato.
Nurettin,

7
Non capisco: accettato e un sacco di voti positivi per un hack "Credo che per quanto tempo questa operazione richiederà hardware sconosciuto"? pv è la risposta corretta IMO (ma lo farà anche bar )
Stephen,

19
La domanda era: "Come si eseguono le barre di avanzamento" con un esempio di copia dei file. Mi sono concentrato sul problema "grafico", non sul calcolo di quanto è lunga un'operazione di copia di file.
Mitch Haile,

73

Potresti anche essere interessato a come fare un filatore :

Posso fare una spinner in Bash?

Sicuro!

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

Ogni volta che il ciclo scorre, mostra il carattere successivo nella stringa sp, avvolgendosi mentre raggiunge la fine. (i è la posizione del carattere corrente da visualizzare e $ {# sp} è la lunghezza della stringa sp).

La stringa \ b è sostituita da un carattere "backspace". In alternativa, puoi giocare con \ r per tornare all'inizio della riga.

Se vuoi che rallenti, inserisci un comando sleep all'interno del loop (dopo la stampa).

Un equivalente POSIX sarebbe:

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

Se hai già un ciclo che fa molto lavoro, puoi chiamare la seguente funzione all'inizio di ogni iterazione per aggiornare lo spinner:

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

15
Versione molto più corta, completamente portatile *: while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*: sleeppotrebbe richiedere ints anziché decimali)
Adam Katz

1
@Daenyth. Grazie. Gentilmente dove dovremmo chiamare il comando di cui abbiamo bisogno per vederlo è progresso usando il codice precedente?
goro,

@goro: nella some_work ...riga sopra; una discussione più dettagliata che si basa su questa utile risposta e sull'utile commento di Adam Katz - con particolare attenzione alla conformità POSIX - è disponibile qui .
mklement0

@AdamKatz: Questa è una semplificazione utile e portatile, ma per abbinare l'approccio di Daenyth lo spinner deve basarsi \bpiuttosto che \r, poiché altrimenti funzionerebbe solo all'inizio di una riga: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- o, se mostra il cursore dietro lo spinner è indesiderato:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0

1
@kaushal - Ctrl + C lo fermerà manualmente. Se si dispone di un lavoro in background, è possibile memorizzare il suo PID ( job=$!) e quindi eseguirlo while kill -0 $job 2>/dev/null;do …, ad esempio: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

Alcuni post hanno mostrato come visualizzare l'avanzamento del comando. Per calcolarlo, devi vedere quanto hai progredito. Sui sistemi BSD alcuni comandi, come dd (1), accettano un SIGINFOsegnale e segnaleranno i loro progressi. Sui sistemi Linux alcuni comandi risponderanno in modo simile a SIGUSR1. Se questa funzione è disponibile, è possibile reindirizzare l'input ddper monitorare il numero di byte elaborati.

In alternativa, è possibile utilizzare lsofper ottenere l'offset del puntatore di lettura del file e quindi calcolare l'avanzamento. Ho scritto un comando, chiamato pmonitor , che mostra l'avanzamento dell'elaborazione di un processo o file specificato. Con esso puoi fare cose, come le seguenti.

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

Sul mio blog appare una versione precedente di script di shell Linux e FreeBSD .


Questo è fantastico, dimentico sempre di reindirizzare le cose tramite pv :-) Penso che il mio comando "stat" funzioni in modo leggermente diverso, la mia versione (Linux) di questo script: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b
unhammer

Ottimo post, lo adoro sempre quando awkè in gioco!
ShellFish,

Questo è fantastico! Grazie per la fantastica sceneggiatura!
thebeagle

47

Ho una funzione di barra di avanzamento facile che ho scritto l'altro giorno:

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

Oppure prendilo da,
https://github.com/fearside/ProgressBar/


puoi spiegare la riga in 1.2.1.1 per favore? Stai eseguendo una sostituzione sed con le variabili _fill e _empty? Non ho capito bene.
Chirag,

Invece di usare sed, sto usando bash "Sostituzione sottostringa" interna, dato che questo è un lavoro facile, preferisco usare le funzioni interne di bash per questo tipo di lavoro. Anche il codice sembra più bello. :-) Controlla qui tldp.org/LDP/abs/html/string-manipulation.html e cerca la sostituzione della sottostringa.
fearside

e $ {_ fill} viene assegnato come $ {_ done} numero di spazi. Questo è bellissimo. Ottimo lavoro amico. Lo userò sicuramente in tutti i miei script bash haha
Chirag,

Ottimo lavoro @fearside! Ho fatto un po 'di modifica per saltare quando _progress non è cambiato rispetto all'ultimo valore, per migliorare la velocità. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

Dolce. La modifica del trattino per rettangolo conferisce un aspetto più professionale:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI

44

usa il comando linux pv:

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

non conosce le dimensioni se si trova nel mezzo del flusso, ma fornisce una velocità e un totale e da lì puoi capire quanto tempo dovrebbe impiegare e ottenere feedback in modo da sapere che non si è bloccato.


32

Stavo cercando qualcosa di più sexy della risposta selezionata, così ha fatto la mia sceneggiatura.

Anteprima

progress-bar.sh in azione

fonte

L'ho messo su 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
}

uso

 progress-bar 100

1
Non capisco come questo sia integrato in alcune elaborazioni in cui la lunghezza del processo non è nota. Come interrompere la barra di avanzamento se il processo è stato completato in precedenza, ad esempio per decomprimere un file.
gennaio

Penso che l'uso dovrebbe essereprogress-bar 100
jirarium

Progressi davvero interessanti. Come può essere legato a una funzione che elabora azioni prolungate su server remoti su ssh? Voglio dire come è possibile misurare i tempi di un aggiornamento (ad esempio) su server remoti?
senza volto,

1
@faceless non è nell'ambito di questo codice che fornisci il tempo e il conto alla rovescia
Édouard Lopez

1
@Fusion è un personaggio unicode (U + 2587 LOWER SEVEN EIGHTHS BLOCK) che dovrebbe essere sicuro per le shell moderne. Fai una prova sul tuo ambiente
Édouard Lopez,

18

Tar GNU ha un'utile opzione che fornisce una funzionalità di una semplice barra di avanzamento.

(...) Un'altra azione di checkpoint disponibile è 'punto' (o '.'). Indica a tar di stampare un singolo punto sul flusso di elenco standard, ad esempio:

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

Lo stesso effetto può essere ottenuto da:

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

+1 per l'approccio più semplice! Se non vedi punti stampati, prova ad esempio a ridurre il numero --checkpoint=.10. Funziona benissimo anche durante l'estrazione con tar -xz.
Noam Manos,

13

Un metodo più semplice che funziona sul mio sistema usando l'utilità 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

13

Non ho visto nulla di simile e tutte le funzioni personalizzate qui sembrano concentrarsi solo sul rendering, quindi ... la mia semplicissima soluzione conforme a POSIX di seguito con spiegazioni dettagliate perché questa domanda non è banale.

TL; DR

Il rendering della barra di avanzamento è molto semplice. Stimare quanto dovrebbe essere reso è una questione diversa. Ecco come visualizzare (animare) la barra di avanzamento: puoi copiare e incollare questo esempio in un file ed eseguirlo:

#!/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} - valori da 1 a 20
  • echo -n - stampa senza nuova riga alla fine
  • echo -e - interpretare caratteri speciali durante la stampa
  • "\r" - ritorno a capo, un carattere speciale per tornare all'inizio della riga

Puoi farlo renderizzare qualsiasi contenuto a qualsiasi velocità, quindi questo metodo è molto universale, ad esempio spesso usato per la visualizzazione di "hacking" nei film sciocchi, senza scherzare.

Risposta completa

Il problema principale è come determinare il $ivalore, ovvero quanto della barra di avanzamento visualizzare. Nell'esempio sopra ho appena lasciato che aumenti in forloop per illustrare il principio, ma un'applicazione di vita reale userebbe un loop infinito e calcolerebbe la $ivariabile su ogni iterazione. Per effettuare tale calcolo sono necessari i seguenti ingredienti:

  1. quanto lavoro c'è da fare
  2. quanto lavoro è stato fatto finora

Nel caso cpsia necessario la dimensione di un file sorgente e la dimensione del file di destinazione:

#!/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 - controlla le statistiche del file
  • -c - restituisce un valore formattato
  • %s - dimensione totale

In caso di operazioni come la decompressione dei file, il calcolo della dimensione di origine è leggermente più difficile ma comunque facile come ottenere la dimensione di un file non compresso:

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
  • gzip -l - visualizza informazioni sull'archivio zip
  • tail -n1 - lavorare con 1 riga dal basso
  • tr -s ' ' - traduci più spazi in uno (schiacciali)
  • cut -d' ' -f3 - tagliare la terza colonna delimitata da spazi

Ecco la carne del problema, però. Questa soluzione è sempre meno generale. Tutti i calcoli dell'avanzamento effettivo sono strettamente legati al dominio che si sta tentando di visualizzare, si tratta di un'operazione a singolo file, un conto alla rovescia del timer, un numero crescente di file in una directory, un'operazione su più file, ecc. Pertanto, non può essere riutilizzato. L'unica parte riutilizzabile è il rendering della barra di avanzamento. Per riutilizzarlo devi estrarlo e salvarlo in un file (ad es. /usr/lib/progress_bar.sh), Quindi definire le funzioni che calcolano i valori di input specifici per il tuo dominio. Ecco come potrebbe apparire un codice generalizzato (ho anche reso $BARdinamico perché le persone lo chiedevano, il resto dovrebbe essere chiaro ormai):

#!/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 - un built-in per la stampa di materiale in un determinato formato
  • printf '%50s' - non stampare nulla, riempirlo con 50 spazi
  • tr ' ' '#' - tradurre ogni spazio in segno di hash

Ed è così che lo useresti:

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

Ovviamente può essere racchiuso in una funzione, riscritto per funzionare con flussi convogliati, riscritto in un'altra lingua, qualunque sia il tuo veleno.


1
Per quelli che vogliono le cose più semplici ho fatto il mio con la prima risposta di cprn. È una barra di avanzamento molto semplice in una funzione che usa una stupida regola di proporzionalità per disegnare la barra: pastebin.com/9imhRLYX
YCN-


9

Barra di avanzamento stile APT (non interrompe l'output normale)

inserisci qui la descrizione dell'immagine

EDIT: per una versione aggiornata controlla la mia pagina github

Non ero soddisfatto delle risposte a questa domanda. Quello che stavo cercando personalmente era una barra di avanzamento elegante come viene visto da APT.

Ho dato un'occhiata al codice sorgente C per APT e ho deciso di scrivere il mio equivalente per bash.

Questa barra di avanzamento rimarrà bene nella parte inferiore del terminale e non interferirà con alcun output inviato al terminale.

Si noti che la barra è attualmente fissata a 100 caratteri di larghezza. Se vuoi ridimensionarlo alle dimensioni del terminale, anche questo è abbastanza facile da realizzare (La versione aggiornata sulla mia pagina github gestisce bene questo).

Pubblicherò la mia sceneggiatura qui. Esempio di utilizzo:

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

Lo script (consiglio vivamente la versione sul mio github):

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

7

Ciò consente di visualizzare che un comando è ancora in esecuzione:

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

Questo creerà un ciclo while infinito che viene eseguito in background e fa eco a un "." ogni secondo. Questo verrà visualizzato .nella shell. Esegui il tarcomando o uno qualsiasi dei comandi che desideri. Quando quel comando termina l'esecuzione, quindi uccidi l'ultimo processo in esecuzione in background, ovvero il ciclo while infinito .


Non è stato possibile avviare un altro processo in background durante l'esecuzione e potenzialmente essere ucciso al posto del ciclo di avanzamento?
Centimane,

Penso che l'idea sia quella di metterlo in una sceneggiatura, quindi questo intrappolerebbe solo un'uscita di quella sceneggiatura.
Iguananaut,

1
Adoro questo comando, lo sto usando nei miei file. Sono solo un po 'a disagio poiché non capisco davvero come funzioni. La prima e la terza riga sono più facili da capire, ma non sono ancora sicuro. So che questa è una vecchia risposta, ma c'è un modo per ottenere una spiegazione diversa orientata verso i neofiti durante la programmazione
Felipe,

1
Questa è l'UNICA vera risposta, in cui altri sono solo barre di avanzamento dei giochi di Scripting 101 che non significano nulla e non servono a programmi reali, unici, non tracciabili (quasi TUTTI). Grazie.
Bekce,

@Felipe, Il ciclo while è un processo in background. $! nella prima trap acquisisce l'ID di processo di quel processo in background e assicura che se il processo corrente / parent termina anche il processo in background muore e non viene lasciato in sospeso. L'istruzione kill termina il processo in background al termine del comando o dei comandi lunghi.
floydn,

7

Ecco come potrebbe apparire

Caricamento di un file

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

In attesa del completamento di un lavoro

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

Semplice funzione che la implementa

Puoi semplicemente copiarlo e incollarlo nel tuo script. Non richiede nient'altro per funzionare.

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"
}

Esempio di utilizzo

Qui, cariciamo un file e ridisegniamo la barra di avanzamento ad ogni iterazione. Non importa quale lavoro viene effettivamente eseguito, purché possiamo ottenere 2 valori: valore massimo e valore corrente.

Nell'esempio seguente il valore massimo è file_sizee il valore corrente viene fornito da una funzione e viene chiamato 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"

Funzione ordinata e semplice. Molte grazie!
Andreas Kraft,

Questo è quello che sto cercando! Grazie mille :)
wajdi_jurry

4

La maggior parte dei comandi unix non ti darà il tipo di feedback diretto da cui puoi farlo. Alcuni ti daranno output su stdout o stderr che puoi usare.

Per qualcosa come tar puoi usare l'opzione -v e reindirizzare l'output a un programma che aggiorna una piccola animazione per ogni riga che legge. Mentre tar scrive un elenco di file che è stato svelato, il programma può aggiornare l'animazione. Per completare una percentuale dovresti conoscere il numero di file e contare le righe.

cp non fornisce questo tipo di output per quanto ne so. Per monitorare l'avanzamento di cp dovresti monitorare i file di origine e di destinazione e controllare le dimensioni della destinazione. È possibile scrivere un piccolo programma c usando la chiamata di sistema stat (2) per ottenere la dimensione del file. Questo leggerebbe la dimensione della fonte, quindi eseguirà il polling del file di destinazione e aggiornerà una barra% completa in base alla dimensione del file scritto fino ad oggi.


4

La mia soluzione visualizza la percentuale del tarball che è attualmente non compresso e scritto. Lo uso quando scrivo immagini di filesystem di root da 2 GB. Hai davvero bisogno di una barra di avanzamento per queste cose. Quello che faccio è usare gzip --listper ottenere la dimensione totale non compressa del tarball. Da quel momento ho calcolato il fattore di blocco necessario per dividere il file in 100 parti. Infine, stampo un messaggio di checkpoint per ciascun blocco. Per un file da 2 GB, questo comporta un blocco di circa 10 MB. Se è troppo grande, puoi dividere BLOCKING_FACTOR per 10 o 100, ma è più difficile stampare un output piuttosto in termini di percentuale.

Supponendo che tu stia usando Bash, puoi usare la seguente funzione shell

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}
}

Bella soluzione ma come fai quando vuoi comprimere una directory?
Samir Sadek,

4

Innanzitutto la barra non è l'unico indicatore di avanzamento di un tubo. L'altro (forse anche più noto) è pv (pipe viewer).

In secondo luogo bar e pv possono essere usati ad esempio in questo modo:

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

o anche:

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

un trucco utile se si desidera utilizzare bar e pv nei comandi che funzionano con file forniti in argomenti, come ad esempio copia file1 file2, è utilizzare la sostituzione del processo :

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

La sostituzione del processo è una cosa magica bash che crea i file temporanei della pipe fifo / dev / fd / e collega lo stdout dal processo eseguito (tra parentesi) attraverso questa pipe e la copia lo vede proprio come un normale file (con un'eccezione, può solo leggerlo in avanti).

Aggiornare:

il comando bar stesso consente anche la copia. After man bar:

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

Ma la sostituzione del processo è secondo me un modo più generico per farlo. Usa il programma cp stesso.


3

Preferisco usare il dialogo con il parametro --gauge . Viene usato molto spesso nelle installazioni di pacchetti .deb e in altre cose di configurazione di base di molte distro. Quindi non è necessario reinventare la ruota ... di nuovo

Basta inserire un valore int compreso tra 1 e 100 @stdin. Un esempio di base e sciocco:

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

Ho questo file / bin / Wait (con chmod u + x permanenti) per scopi di cottura: 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"

Quindi posso mettere:

Wait "34 min" "warm up the oven"

o

Wait "dec 31" "happy new year"


2

per me il più semplice da usare e il più bello finora è il comando pvo barcome alcuni ragazzi hanno già scritto

ad esempio: è necessario eseguire un backup dell'intera unità con dd

normalmente usi dd if="$input_drive_path" of="$output_file_path"

con pvte puoi farlo così:

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

e i progressi vanno direttamente a STDOUTquesto:

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

dopo aver fatto il riepilogo viene visualizzato

    15654912+0 records in
    15654912+0 records out
    8015314944 bytes (8.0 GB) copied, 2020.49 s, 4.0 MB/s

È possibile utilizzare pvo barvisualizzare l'avanzamento di diversi processi, ad esempio il conto alla rovescia del timer, la posizione in un file di testo, l'installazione dell'app, la configurazione del runtime, ecc.?
cprn,

2

Molte risposte descrivono la scrittura dei propri comandi per la stampa '\r' + $some_sort_of_progress_msg. Il problema a volte è che stampare centinaia di questi aggiornamenti al secondo rallenterà il processo.

Tuttavia, se uno qualsiasi dei tuoi processi produce output (ad es. 7z a -r newZipFile myFolderProdurrà ogni nome di file man mano che lo comprime) allora esiste una soluzione più semplice, veloce, indolore e personalizzabile.

Installa il modulo 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

Aiuto: tqdm -h. Un esempio che utilizza più opzioni:

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

Come bonus puoi anche usare tqdmper avvolgere iterables in codice Python.

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


Non credo che il tuo esempio con "più opzioni" funzioni. Sembra passare lo tqdmSTDOUT wc -lattraverso un tubo. Probabilmente vuoi scappare.
cprn,

1
@cprn tqdmmostrerà progressi STDERRdurante il piping del suo input STDINa STDOUT. In questo caso wc -ldovrebbe ricevere lo stesso input come se tqdmnon fosse incluso.
casper.dcl

Ah, ha un senso adesso. Grazie per aver spiegato.
cprn

2

Sulla base del lavoro di Edouard Lopez, ho creato una barra di avanzamento che si adatta alle dimensioni dello schermo, qualunque esso sia. Controlla.

inserisci qui la descrizione dell'immagine

È anche pubblicato su 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

Godere



1

Questo è applicabile solo usando zenity di gnome. Zenity fornisce un'ottima interfaccia nativa per bash degli script: https://help.gnome.org/users/zenity/stable/

Dall'esempio della barra di avanzamento Zenity:

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

1

Ho usato una risposta da Creare una stringa di caratteri ripetuti nello script della shell per ripetere il carattere. Ho due versioni bash relativamente piccole per gli script che devono visualizzare la barra di avanzamento (ad esempio, un ciclo che attraversa molti file, ma non è utile per file tar di grandi dimensioni o operazioni di copia). La più veloce è composta da due funzioni, una per preparare le stringhe per la visualizzazione della barra:

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

e uno per visualizzare una barra di avanzamento:

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
}

Potrebbe essere usato come:

preparebar 50 "#"

il che significa preparare stringhe per barra con 50 "#" caratteri, e successivamente:

progressbar 35 80

visualizzerà il numero di caratteri "#" che corrispondono al rapporto 35/80:

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

Tenere presente che la funzione visualizza la barra sulla stessa riga più e più volte finché l'utente (o qualche altro programma) non stampa una nuova riga. Se metti -1 come primo parametro, la barra verrebbe cancellata:

progressbar -1 80

La versione più lenta è tutta in una funzione:

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
}

e può essere usato come (lo stesso esempio sopra):

progressbar 35 80 50

Se hai bisogno di progressbar su stderr, aggiungi semplicemente >&2alla fine di ogni comando printf.


1

Per indicare l'avanzamento dell'attività, provare i seguenti comandi:

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;

O

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;

O

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;

O

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;

Si possono usare flag / variabili all'interno del ciclo while per verificare e visualizzare il valore / l'entità del progresso.


1

Utilizzando i suggerimenti sopra elencati, ho deciso di implementare la mia barra di avanzamento.

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

1
Bello! per farlo funzionare ho dovuto cambiare la linea percent_none="$(( 100 - "$percent_done" ))"inpercent_none="$(( 100 - $percent_done))"
sergio

0

Ho realizzato una versione shell pura per un sistema incorporato sfruttando:

  • La funzione di gestione del segnale SIGUSR1 di / usr / bin / dd.

    Fondamentalmente, se si invia un 'kill SIGUSR1 $ (pid_of_running_dd_process)', verrà generato un riepilogo della velocità di elaborazione e dell'importo trasferito.

  • mettere in background dd e quindi interrogarlo regolarmente per gli aggiornamenti e generare tick di hash come facevano i client ftp della vecchia scuola.

  • Utilizzo di / dev / stdout come destinazione per programmi non stdout compatibili come scp

Il risultato finale ti consente di eseguire qualsiasi operazione di trasferimento di file e ottenere un aggiornamento dei progressi che assomiglia all'output 'hash' FTP della vecchia scuola in cui avresti solo un segno di hash per ogni X byte.

Non si tratta di un codice di qualità della produzione, ma hai capito. Penso che sia carino.

Per quello che vale, l'effettivo conteggio dei byte potrebbe non riflettersi correttamente nel numero di hash - potresti averne uno in più o in meno a seconda dei problemi di arrotondamento. Non usarlo come parte di uno script di test, è solo un piacere per gli occhi. E sì, sono consapevole che questo è terribilmente inefficiente: è uno script di shell e non mi scuso.

Esempi con wget, scp e tftp forniti alla fine. Dovrebbe funzionare con tutto ciò che ha emesso dati. Assicurati di usare / dev / stdout per programmi che non sono stdout-friendly.

#!/bin/sh
#
# Copyright (C) Nathan Ramella (nar+progress-script@remix.net) 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
}

Esempi:

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

Idea decente, fintanto che hai le dimensioni del file in anticipo, puoi fornire un valore aggiunto rispetto a pv in questo modo, ma segnalando ciecamente che pidof ddè spaventoso.

Tentativo di chiamarlo fuori con "# La mia gestione PID è stupida"
synthesizerpatel,

È forse possibile catturare $!da dde aspettare [[ -e /proc/${DD_PID} ]].

0

Nel caso in cui sia necessario mostrare una barra di avanzamento temporale (conoscendo in anticipo il tempo di visualizzazione), è possibile utilizzare Python come segue:

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

Quindi, supponendo di aver salvato lo script Python come progressbar.py, è possibile mostrare la barra di avanzamento dallo script bash eseguendo il comando seguente:

python progressbar.py 10 50

Mostrerebbe una barra di avanzamento dei 50caratteri delle dimensioni e "in esecuzione" per 10secondi.


0

Ho costruito sulla risposta fornita da fearside

Ciò si collega a un database Oracle per recuperare lo stato di avanzamento di un ripristino 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'

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

}

potrebbe creare una funzione che la disegna su una scala diciamo 1-10 per il numero di barre:

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

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;

produzione:

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

nota: se invece di 255 inserisci 1 monitorerai lo standard in ... con 2 lo standard out (ma devi modificare l'origine per impostare "tot" sulla dimensione del file di output proiettato)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.