Domanda

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 mi consiglia di aggiungere barre di avanzamento agli script di shell?

È stato utile?

Soluzione

Puoi implementarlo sovrascrivendo una riga. Usa \ r per tornare all'inizio della riga senza scrivere \ n sul terminale.

Scrivi \ n quando hai finito per far avanzare la linea.

Usa echo -ne per:

  1. non stampa \ n e
  2. per 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 "errore". se inizi con una linea lunga e poi vuoi scrivere una linea corta: in questo caso, dovrai sovrascrivere la lunghezza della linea lunga (ad es. con spazi).

Altri suggerimenti

Potresti anche essere interessato a come fare un filatore :

Posso fare una spinner in Bash?

  

Certo!

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

Ogni volta che il ciclo viene ripetuto, viene visualizzato il carattere successivo nello sp   stringa, avvolgendosi mentre raggiunge la fine. (i è la posizione di   il carattere corrente da visualizzare e $ {# sp} è la lunghezza dello sp   stringa).

     

La stringa \ b è sostituita da un carattere "backspace". In alternativa,   potresti 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 loop che fa molto lavoro, puoi chiamare il   seguente funzione all'inizio di ogni iterazione per aggiornare il file   filatore:

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

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 segnale SIGINFO e segnaleranno i loro progressi. Sui sistemi Linux alcuni comandi rispondono in modo simile a SIGUSR1 . Se questa funzione è disponibile, è possibile reindirizzare l'input tramite dd per monitorare il numero di byte elaborati.

In alternativa, puoi utilizzare lsof per ottenere l'offset di il 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%

Una versione precedente degli script della shell Linux e FreeBSD appare sul il mio blog .

usa il comando linux pv:

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

non conosce le dimensioni se si trova nel mezzo dello stream, 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.

Ho ottenuto una semplice barra di avanzamento 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-

Ho ottenuto una semplice barra di avanzamento che ho scritto l'altro giorno:

<*>

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

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'

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

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

Anteprima

 progress-bar.sh in azione

sorgente

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

GNU tar ha un'opzione utile che offre 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

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

Vorrei anche contribuire con i miei progressi personali bar

Raggiunge la precisione dei sotto-caratteri usando Mezzi blocchi unicode

 inserisci qui la descrizione dell'immagine

Il codice è incluso

Questo ti permette di visualizzare che un comando sta ancora eseguendo:

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 infinito ciclo while che viene eseguito in background e fa eco a ". " ogni secondo. Questo mostrerà . nella shell. Esegui il comando tar o qualsiasi altro comando tu voglia. Al termine dell'esecuzione di quel comando, uccidi l'ultimo processo in esecuzione in background, ovvero il infinito ciclo while .

Non ho visto niente di simile, quindi ... la mia soluzione molto semplice:

#!/bin/bash
BAR='####################'   # this is full bar, mine is 20 chars
for i in {1..20}; do
    echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
    sleep .1
done
  • echo -n - stampa senza una nuova riga alla fine
  • echo -e - interpreta i caratteri speciali durante la stampa
  • " \ r " - ritorno a capo, un carattere speciale per tornare all'inizio della riga

L'ho usato molto tempo fa in un semplice video di "hacking" per simulare il codice di battitura. ;)

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. Puoi scrivere un piccolo programma c usando la stat (2) per ottenere il dimensione del file. Questo leggerebbe la dimensione della fonte, quindi eseguirà il polling del file di destinazione e aggiornerebbe una barra% completa in base alla dimensione del file scritto fino ad oggi.

La mia soluzione visualizza la percentuale del tarball che è attualmente non compresso e scritto. Io lo uso quando si scrivono immagini di filesystem di root da 2 GB. Tu veramente serve una barra di avanzamento per queste cose. Quello che faccio è usare gzip --list per ottenere la dimensione totale non compressa di tarball. Da questo calcolo il fattore di blocco necessario per dividere il file in 100 parti. Infine, stampo a messaggio di checkpoint per ogni blocco. Per un file da 2 GB questo dà circa 10 MB un blocco. Se è troppo grande, puoi farlo dividi BLOCKING_FACTOR per 10 o 100, ma poi lo è più difficile da stampare piuttosto in termini di percentuale.

Supponendo che tu stia usando Bash, puoi usare il 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}
}

Prima di tutto 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 vuoi usare bar e pv nei comandi che funzionano con i file forniti in argomenti, come ad es. copia file1 file2, deve utilizzare sostituzione del processo :

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

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

Aggiornamento:

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 a mio avviso la sostituzione del processo è un modo più generico per farlo. Un programma utilizza cp stesso.

Preferisco usare dialog 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:

Attendi "34 min" " riscalda il forno "

o

Attendi "dic 31" " felice anno nuovo "

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 nello 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 (( 

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 nello script. Non richiede nient'altro per funzionare.

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

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_max < 1 )); then __max=1; fi # anti zero division protection local __percentage=$(( 100 - (

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_max*100 -

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_value*100) /

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_max )) # Rescale the bar according to the progress bar width local __num_bar=$((

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_percentage * $PROGRESS_BAR_WIDTH / 100 )) # Draw progress bar printf "[" for b in $(seq 1

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_num_bar); do printf "#"; done for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH -

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_num_bar ))); do printf " "; done printf "]

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_percentage%% (

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_value /

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_max

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 nello script. Non richiede nient'altro per funzionare.

<*>

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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>_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 è dimensione_file e il valore corrente viene fornito da alcune funzioni e si chiama upload_bytes .

<*>

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 lo gestisce bene).

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

per me il più facile da usare e il più bello finora è il comando pv o bar come alcuni ragazzi hanno già scritto

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

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

con pv puoi farlo in questo modo:

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

e il progresso passa direttamente a STDOUT come questo:

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

dopo aver fatto il riepilogo compare

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

Molte risposte descrivono come scrivere i propri comandi per stampare '\ 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 (es. 7z a -r newZipFile myFolder produrrà 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 tqdm per racchiudere gli iterabili nel codice python.

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

Questo è applicabile solo usando zenity di gnome. Zenity offre 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

Ho usato una risposta da Creazione di una stringa di caratteri ripetuti nello script shell per la ripetizione del 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 "#"

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:

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

Tieni presente che la funzione visualizza la barra sulla stessa riga più e più volte finché tu (o qualche altro programma) non stampi una nuova riga. Se si inserisce -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 della barra di avanzamento su stderr, aggiungi > & amp; 2 alla fine di ogni comando printf.

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;

È possibile utilizzare flag / variabili all'interno del ciclo while per verificare e visualizzare il valore / l'entità dei progressi.

Basato sul lavoro di Edouard Lopez, ho creato una barra di avanzamento che si adatta alle dimensioni dello schermo, qualunque esso sia. Dai un'occhiata.

 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: <*> [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

Godetevi

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 "$@"

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

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

    Fondamentalmente, se invii un 'kill SIGUSR1 $ (pid_of_running_dd_process)', verrà emesso un riepilogo della velocità effettiva e dell'importo trasferito.

  • backgrounding dd e quindi interrogandolo regolarmente per gli aggiornamenti e generando hash tick 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 aggiornamenti sui progressi che sembrano output 'hash' FTP della vecchia scuola in cui avresti semplicemente un segno di hash per ogni X byte.

Questo è quasi un codice di qualità di 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

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 che tu abbia salvato lo script Python come progressbar.py , è possibile mostrare la barra di avanzamento dal tuo script bash eseguendo il comando seguente:

python progressbar.py 10 50

Mostrerebbe una barra di avanzamento con caratteri 50 e " in esecuzione " per 10 secondi.

Ho sviluppato la risposta fornita da fearside

Questo si collega a un database Oracle per recuperare l'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-

Ho sviluppato la risposta fornita da fearside

Questo si collega a un database Oracle per recuperare l'avanzamento di un ripristino RMAN.

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

https://github.com/extensionsapp/progre.sh

Crea progressi del 40 percento: progreSh 40

 inserisci qui la descrizione dell'immagine

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

uscita:

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 la sorgente per impostare " tot " sulla dimensione del file di output proiettato)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top