使用Bash显示进度指示器

使用Bash显示进度指示器,bash,shell,unix,progress,Bash,Shell,Unix,Progress,使用仅限bash的脚本,如何提供bash进度指示器 例如,当我从bash运行一个命令时(当该命令正在执行时),让用户知道某些事情仍在发生。在这个使用SCP的示例中,我演示了如何获取进程id(pid),然后在该进程运行时执行某些操作 这将显示一个简单的微调图标 /usr/bin/scp me@website.com:file somewhere 2>/dev/null & pid=$! # Process Id of the previous running command spi

使用仅限bash的脚本,如何提供bash进度指示器


例如,当我从bash运行一个命令时(当该命令正在执行时),让用户知道某些事情仍在发生。

在这个使用SCP的示例中,我演示了如何获取进程id(pid),然后在该进程运行时执行某些操作

这将显示一个简单的微调图标

/usr/bin/scp me@website.com: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

威廉·珀塞尔的解决方案

/usr/bin/scp me@website.com: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

如果您有一种估算完成百分比的方法,例如当前处理的文件数和总数,您可以制作一个简单的线性进度表,其中包含一些关于屏幕宽度的数学和假设

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
或者,你可以估算剩余时间,而不是线性仪表。它和其他类似的东西一样精确

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"
这是一个很好的微调器函数(稍加修改),可以帮助光标保持在原始位置

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"
}
使用方法:

(a_long_running_task) &
spinner

@DavidD对Pez Cuckows答案的评论,这是一个示例,说明如何在脚本中捕获进度条的输出,并且仍然可以在屏幕上看到微调器:

#!/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 $@, the rest are the options. 
declare    CMD_OPTIONS="$@"
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 
#/usr/bin/env bash
#############################################################################
###########################################################################
###
###由A.M.Danischewski(c)2015 v1.1修改/重写
###问题:如果你发现任何问题,请告诉我
###在gmail.com。
###
###根据Pez Cuckow,William Pursell发布的脚本:
### http://stackoverflow.com/questions/12498304/using-bash-to-display-\
###a-进展-工作-指标
###
###此程序运行传入的程序,并输出
###命令和it exec是stdout的新fd,因此您可以分配
###变量正在运行的内容的输出。
### 
###这是一个非常新的草稿,但可以扩展。
### 
###此程序是免费软件:您可以重新发布和/或修改它
###它是根据GNU通用公共许可证的条款发布的
自由软件基金会,或者许可证的第3版,或者
###(由您选择)任何更高版本。
###
###这个节目的发布是希望它会有用,
###但无任何保证;甚至没有任何关于
###适销性或适合某一特定目的。见
###有关更多详细信息,请参阅GNU通用公共许可证。
###
###您应该已经收到GNU通用公共许可证的副本
###和这个节目一起。如果没有,请参阅。
###########################################################################
#############################################################################
declare CMD=“${1}”
shift##剪裁$@的第一个值,其余为选项。
声明CMD_OPTIONS=“$@”
声明CMD_OUTPUT=“”
声明TMP_输出=“/TMP/${0##*/}$$$(日期+%Y%m%d%H%m%S%N)”
声明-r SPIN_DELAY=“0.1”
声明-i PID=
函数用法(){
cat$var=\$(${0##*/}睡眠5\&\&echo hi)
正在运行:睡眠5和回声hi,PID 32229:-
实0m5.003s
用户0.000s
系统0m0.001s
完成。。
>$echo\$var
你好
EOF
} 
函数spin_wait(){
本地-旋转
自旋[0]=“-”
自旋[1]=“\\”
自旋[2]=“|”
自旋[3]=“/”
echo-en“Running:${CMD}${CMD_OPTIONS},PID${PID}:”>&3
而kill-0${PID}2>/dev/random;do
对于“${spin[@]}”中的i,请执行以下操作
echo-ne“\b$i”>&3
睡眠${SPIN_DELAY}
完成
完成
} 
函数run_cmd(){
执行3>$(tty)
eval“time${CMD}${CMD_OPTIONS}”2>>“${TMP_OUTPUT}”| tee“${TMP_OUTPUT}”和
PID=$!#将全局PID设置为我们刚才运行的命令的进程id。
旋转,等等
echo-en“\n$(<“${TMP\u OUTPUT}”)\n“>&3
echo-en“完成…”\n“>&3
rm“${TMP_输出}”
执行官3>&-
} 
如果[[-z“${CMD}”| |“${CMD}”=~^-.];然后
用法|更多&&退出0
其他的
运行命令
fi
出口0

这里是一个简单的在线工具,我使用:

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

这是我的尝试。我是bash脚本的新手,因此有些代码可能很糟糕:)

示例输出:

守则:

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
您可以在此处看到源代码:
这是一项非常简单的技术:
(只要用您想要指示正在运行的任何命令替换
sleep 20

输出如下所示:

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

它添加了一个
(高密度虚线)每秒钟一次,直到过程完成

bash脚本的迷幻进度条。通过命令行以“./progressbar x y”形式调用,其中“x”是以秒为单位的时间,“y”是要显示的消息。内部函数progressbar()也可以独立工作,并将“x”作为百分比,“y”作为消息

#!/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"
#/bin/bash
如果[“$#”-等式0];然后回显“x是时间单位秒”,z是信息单位;echo“用法:progressbar x z”;出口fi
progressbar(){
本地loca=$1;本地loca2=$2;
声明-a bgcolors;声明-a fgcolors;
因为我在{40..46}{100..106};做什么
bgcolors+=(“$i”)
完成
因为我在{30..36}{90..96};做什么
fgcolors+=(“$i”)
完成
本地单位=$((50-loca));
局部y;局部t;
本地z;z=$(printf“%*s'$u”);
本地水资源=$((loca*2));
本地弹跳器=“.oO°oO。”;

对于((i=0;i以下是通过linux“speedtest cli”命令进行internet连接速度测试的“活动指示器”示例:

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
更新-示例:

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

除了经典的微调器,您还可以使用

它通过使用

链接中包含的代码。

我通过在微调器后显示变量信息来扩展in的答案:

#!/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..."
!/usr/bin/env bash
函数微调器(){
本地信息=“$1”
本地pid=$!
本地延迟=0.75
局部spinstr='|/-\'
而kill-0$pid 2>/dev/null;do
本地温度=${spinstr#?}
printf“[%c]$info”“$spinstr”
本地spinstr=$temp${spinstr%“$temp”}
睡眠延迟