wake-up-neo.net

Verwenden von BASH zum Anzeigen einer Statusanzeige (Arbeitsanzeige)

Wie kann ein Bash-Fortschrittsindikator verwendet werden?

Ich kann also ein Befehlsformular bash ausführen, und während der Befehl ausgeführt wird, sollte der Benutzer wissen, dass noch etwas passiert.

43
Pez Cuckow

In diesem Beispiel mit SCP demonstriere ich, wie Sie die Prozess-ID (PID) erfassen und dann etwas tun, während der Prozess ausgeführt wird.

Dies zeigt ein einfaches Spinnng-Symbol an.

/usr/bin/scp [email protected]:file somewhere 2>/dev/null &
pid=$! # Process Id of the previous running command

spin[0]="-"
spin[1]="\\"
spin[2]="|"
spin[3]="/"

echo -n "[copying] ${spin[0]}"
while [ kill -0 $pid ]
do
  for i in "${spin[@]}"
  do
        echo -ne "\b$i"
        sleep 0.1
  done
done

William Pursells Lösung

/usr/bin/scp [email protected]:file somewhere 2>/dev/null &
pid=$! # Process Id of the previous running command

spin='-\|/'

i=0
while kill -0 $pid 2>/dev/null
do
  i=$(( (i+1) %4 ))
  printf "\r${spin:$i:1}"
  sleep .1
done
61
Pez Cuckow

Wenn Sie den prozentualen Wert schätzen können, z. B. die aktuelle Anzahl der verarbeiteten Dateien und die Gesamtzahl, können Sie mit einfachen Berechnungen und Annahmen zur Bildschirmbreite eine einfache lineare Fortschrittsanzeige erstellen.

count=0
total=34
pstr="[=======================================================================]"

while [ $count -lt $total ]; do
  sleep 0.5 # this is work
  count=$(( $count + 1 ))
  pd=$(( $count * 73 / $total ))
  printf "\r%3d.%1d%% %.${pd}s" $(( $count * 100 / $total )) $(( ($count * 1000 / $total) % 10 )) $pstr
done

Oder Sie können anstelle eines Längenmessers die verbleibende Zeit abschätzen. Es ist ungefähr so ​​genau wie andere ähnliche Dinge.

count=0
total=34
start=`date +%s`

while [ $count -lt $total ]; do
  sleep 0.5 # this is work
  cur=`date +%s`
  count=$(( $count + 1 ))
  pd=$(( $count * 73 / $total ))
  runtime=$(( $cur-$start ))
  estremain=$(( ($runtime * $total / $count)-$runtime ))
  printf "\r%d.%d%% complete ($count of $total) - est %d:%0.2d remaining\e[K" $(( $count*100/$total )) $(( ($count*1000/$total)%10)) $(( $estremain/60 )) $(( $estremain%60 ))
done
printf "\ndone\n"
20
evil otto

Von hier wird eine Nice-Spinner-Funktion (mit geringfügigen Modifikationen) empfohlen, die dazu beiträgt, dass der Cursor in der ursprünglichen Position bleibt.

spinner()
{
    local pid=$!
    local delay=0.75
    local spinstr='|/-\'
    while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do
        local temp=${spinstr#?}
        printf " [%c]  " "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        printf "\b\b\b\b\b\b"
    done
    printf "    \b\b\b\b"
}

bei Verwendung:

(a_long_running_task) &
spinner
16
checksum

Dies ist eine ziemlich einfache Technik:
(Ersetzen Sie einfach sleep 20 mit dem Befehl, den Sie ausführen möchten.)

#!/bin/bash

sleep 20 & PID=$! #simulate a long process

echo "THIS MAY TAKE A WHILE, PLEASE BE PATIENT WHILE ______ IS RUNNING..."
printf "["
# While process is running...
while kill -0 $PID 2> /dev/null; do 
    printf  "▓"
    sleep 1
done
printf "] done!"

Die Ausgabe sieht folgendermaßen aus:

> THIS MAY TAKE A WHILE, PLEASE BE PATIENT WHILE ______ IS RUNNING...
> [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] done!

Es fügt jede Sekunde einen (Punkt mit hoher Dichte) hinzu, bis der Vorgang abgeschlossen ist. 

10
cosbor11

Hier ein einfacher Onliner, den ich benutze:

while true; do for X in '-' '/' '|' '\'; do echo -en "\b$X"; sleep 0.1; done; done 
5
fipsbox

Hier ist mein Versuch. Ich bin neu in Bash-Skripten, daher kann ein Teil dieses Codes schrecklich sein :)

Beispielausgabe:

 In Progress  Done

Der Code:

progressBarWidth=20

# Function to draw progress bar
progressBar () {

  # Calculate number of fill/empty slots in the bar
  progress=$(echo "$progressBarWidth/$taskCount*$tasksDone" | bc -l)  
  fill=$(printf "%.0f\n" $progress)
  if [ $fill -gt $progressBarWidth ]; then
    fill=$progressBarWidth
  fi
  empty=$(($fill-$progressBarWidth))

  # Percentage Calculation
  percent=$(echo "100/$taskCount*$tasksDone" | bc -l)
  percent=$(printf "%0.2f\n" $percent)
  if [ $(echo "$percent>100" | bc) -gt 0 ]; then
    percent="100.00"
  fi

  # Output to screen
  printf "\r["
  printf "%${fill}s" '' | tr ' ' ▉
  printf "%${empty}s" '' | tr ' ' ░
  printf "] $percent%% - $text "
}



## Collect task count
taskCount=33
tasksDone=0

while [ $tasksDone -le $taskCount ]; do

  # Do your task
  (( tasksDone += 1 ))

  # Add some friendly output
  text=$(echo "somefile-$tasksDone.dat")

  # Draw the progress bar
  progressBar $taskCount $taskDone $text

  sleep 0.01
done

echo

Sie können die Quelle hier sehen: https://Gist.github.com/F1LT3R/fa7f102b08a514f2c535

4
f1lt3r

Neben dem klassischen Spinner können Sie diesen Fortschrittsbalken verwenden

Es erreicht eine Unterzeichengenauigkeit durch Verwendung von Halbblockzeichen

 enter image description here

Code auf dem Link enthalten.

3
nachoparker

Hier ist ein Beispiel eines Aktivitätsindikators für einen Geschwindigkeitstest der Internetverbindung über den Linux-Befehl speedtest-cli:

printf '\n\tInternet speed test:  '

# http://stackoverflow.com/questions/12498304/using-bash-to-display-a-progress-working-indicator

spin[0]="-"
spin[1]="\\"
spin[2]="|"
spin[3]="/"

# http://stackoverflow.com/questions/20165057/executing-bash-loop-while-command-is-running

speedtest > .st.txt &           ## & : continue running script
pid=$!                          ## PID of last command

# If this script is killed, kill 'speedtest':
trap "kill $pid 2> /dev/null" EXIT

# While 'speedtest' is running:
while kill -0 $pid 2> /dev/null; do
for i in "${spin[@]}"
do
    echo -ne "\b$i"
    sleep 0.1
done
done

# Disable the trap on a normal exit:
trap - EXIT

printf "\n\t           "
grep Download: .st.txt
printf "\t             "
grep Upload: .st.txt
echo ''
rm -f st.txt

Update - Beispiel:

 animated GIF

1
Victoria Stuart

Psychedelischer Fortschrittsbalken für Bash-Scripting. Aufruf über die Befehlszeile als './progressbar x y', wobei 'x' eine Zeit in Sekunden und 'y' eine anzuzeigende Nachricht ist. Die innere Funktion progressbar () funktioniert ebenfalls eigenständig und nimmt 'x' als Prozentwert und 'y' als Nachricht an.

#!/bin/bash

if [ "$#" -eq 0 ]; then echo "x is \"time in seconds\" and z is \"message\""; echo "Usage: progressbar x z"; exit; fi
progressbar() {
        local loca=$1; local loca2=$2;
        declare -a bgcolors; declare -a fgcolors;
        for i in {40..46} {100..106}; do
                bgcolors+=("$i")
        done
        for i in {30..36} {90..96}; do
                fgcolors+=("$i")
        done
        local u=$(( 50 - loca ));
        local y; local t;
        local z; z=$(printf '%*s' "$u");
        local w=$(( loca * 2 ));
        local bouncer=".oO°Oo.";
        for ((i=0;i<loca;i++)); do
                t="${bouncer:((i%${#bouncer})):1}"
                bgcolor="\\E[${bgcolors[RANDOM % 14]}m \\033[m"
                y+="$bgcolor";
        done
        fgcolor="\\E[${fgcolors[RANDOM % 14]}m"
        echo -ne " $fgcolor$t$y$z$fgcolor$t \\E[96m(\\E[36m$w%\\E[96m)\\E[92m $fgcolor$loca2\\033[m\r"
};
timeprogress() {
        local loca="$1"; local loca2="$2";
        loca=$(bc -l <<< scale=2\;"$loca/50")
        for i in {1..50}; do
                progressbar "$i" "$loca2";
                sleep "$loca";
        done
        echo -e "\n"
};
timeprogress "$1" "$2"
0
nexace

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

Erstelle 82 Prozent Fortschritt: progreSh 82

 enter image description here

0
Hello World

@ DavidDs Kommentare zur Antwort von Pez Cuckows. Dies ist ein Beispiel, wie Sie die Ausgabe der Fortschrittsleiste in einem Skript erfassen und trotzdem den Spinner auf dem Bildschirm sehen können: 

#!/usr/bin/env bash 

#############################################################################
###########################################################################
###
### Modified/Rewritten by A.M.Danischewski (c) 2015 v1.1
### Issues: If you find any issues emai1 me at my <first name> dot 
###         <my last name> at gmail dot com.  
###
### Based on scripts posted by Pez Cuckow, William Pursell at:  
### http://stackoverflow.com/questions/12498304/using-bash-to-display-\
###      a-progress-working-indicator
###
### This program runs a program passed in and outputs a timing of the 
### command and it exec's a new fd for stdout so you can assign a 
### variable the output of what was being run. 
### 
### This is a very new rough draft but could be expanded. 
### 
### This program is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with this program.  If not, see <http://www.gnu.org/licenses/>.
###########################################################################
#############################################################################

declare    CMD="${1}"
shift      ## Clip the first value of the [email protected], the rest are the options. 
declare    CMD_OPTIONS="[email protected]"
declare    CMD_OUTPUT=""
declare    TMP_OUTPUT="/tmp/_${0##*/}_$$_$(date +%Y%m%d%H%M%S%N)" 
declare -r SPIN_DELAY="0.1"
declare -i PID=

function usage() {
cat <<EOF

Description: ${0##*/}

This program runs a program passed in and outputs a timing of the 
command and it exec's a new fd for stdout so you can assign a variable 
the output of what was being run. 

Usage: ${0##*/} <command> [command options]

 E.g.  
    >$ ${0##*/} sleep 5 \&\& echo "hello" \| figlet
     Running: sleep 5 && echo hello | figlet, PID 2587:/

     real   0m5.003s
     user   0m0.000s
     sys    0m0.002s
      _          _ _       
     | |__   ___| | | ___  
     | '_ \ / _ \ | |/ _ \ 
     | | | |  __/ | | (_) |
     |_| |_|\___|_|_|\___/ 

     Done..
    >$ var=\$(${0##*/} sleep 5 \&\& echo hi)
     Running: sleep 5 && echo hi, PID 32229:-
     real   0m5.003s
     user   0m0.000s
     sys    0m0.001s
     Done..
     >$ echo \$var
     hi

EOF
} 

function spin_wait() { 
 local -a spin 
 spin[0]="-"
 spin[1]="\\"
 spin[2]="|"
 spin[3]="/"
 echo -en "Running: ${CMD} ${CMD_OPTIONS}, PID ${PID}: " >&3
 while kill -0 ${PID} 2>/dev/random; do
   for i in "${spin[@]}"; do
     echo -ne "\b$i" >&3
     sleep ${SPIN_DELAY}
   done
 done
} 

function run_cmd() { 
 exec 3>$(tty)
 eval "time ${CMD} ${CMD_OPTIONS}" 2>>"${TMP_OUTPUT}" | tee "${TMP_OUTPUT}" & 
 PID=$! # Set global PID to process id of the command we just ran. 
 spin_wait
 echo -en "\n$(< "${TMP_OUTPUT}")\n" >&3 
 echo -en "Done..\n" >&3
 rm "${TMP_OUTPUT}"
 exec 3>&-
} 

if [[ -z "${CMD}" || "${CMD}" =~ ^-. ]]; then 
 usage | more && exit 0 
else 
 run_cmd  
fi 

exit 0 
0
user4401178

Ich habe die Antwort von checksum in seiner Antwort durch die Anzeige einer variablen Infonachricht nach dem Spinner erweitert:

#!/usr/bin/env bash 
function spinner() {
    local info="$1"
    local pid=$!
    local delay=0.75
    local spinstr='|/-\'
    while kill -0 $pid 2> /dev/null; do
        local temp=${spinstr#?}
        printf " [%c]  $info" "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        local reset="\b\b\b\b\b\b"
        for ((i=1; i<=$(echo $info | wc -c); i++)); do
            reset+="\b"
        done
        printf $reset
    done
    printf "    \b\b\b\b"
}

# usage:
(a_long_running_task) &
spinner "performing long running task..."

Das gefällt mir nicht, wenn die stdout-Ausgabe mit einem Spinner in eine Datei umgeleitet wird. less zeigt ^H für jeden Rückschritt an, anstatt sie in einer Dateiausgabe überhaupt zu vermeiden. Ist das mit einem einfachen Spinner wie diesem möglich?

0
jan