シェルスクリプトにプログレスバーを追加する方法


413

* NIXのbashまたはその他のシェルでスクリプトを作成する場合、数秒以上かかるコマンドを実行している間、進行状況バーが必要です。

たとえば、大きなファイルをコピーしたり、大きなtarファイルを開いたりします。

シェルスクリプトに進行状況バーを追加するには、どのような方法をお勧めしますか?


制御ロジックの例については、stackoverflow.com / questions / 12498304 /…も参照してください(ジョブをバックグラウンドで実行し、完了するまで何かを行います)。
tripleee 2015

1
スクリプトを作成するときに頻繁に役立つ一連の要件があります。ロギング、進行状況、色、ファンシーな出力などの表示...ある種の単純なスクリプトフレームワークがあるべきだといつも思っていました。最後に、何も見つからなかったので実装することにしました。これは役に立つかもしれません。それは純粋なbashです、つまりJust Bashです。github.com/SumuduLansakara/JustBash
Sumudu 2017

これをunix.stackexchange.comに移動しないでください
イーサン

回答:


685

これを実装するには、行を上書きします。\r何も書かずに行の先頭に戻るために使用します\n端末にことます。

書く \nあなたが行を進めるために行われているとき。

使用echo -neする:

  1. 印刷しない \n
  2. のようなエスケープシーケンスを認識し\rます。

ここにデモがあります:

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

以下のコメントで、長い行から始めて短い行を書きたい場合、pukはこの「失敗」について言及します。この場合、長い行の長さ(スペースなど)を上書きする必要があります。


23
(少なくともMacOS Xでは)echo manページによると、sh / bashは「-n」を受け入れない独自の組み込みechoコマンドを使用します...したがって、同じことを行うには、\ r \ r
Justin Jenkins

51
これを出力するポータブルな方法は、のprintf代わりにを使用することですecho
イェンス

9
printfの場合、次の形式を使用するprintf "#### (50%%)\r"必要があります。、単一引用符では機能せず、パーセント記号をエスケープする必要があります。
ヌレティン

7
私はこれを受け取りません-「この操作が未知のハードウェアでどのくらいの時間がかかるか推測する」ハックのための受け入れられた賛成票の山?pvは正解IMOです(ただし、バーも使用できます)
Stephen

19
問題は、ファイルのコピーの例を示す「プログレスバーの操作方法」です。私は「グラフィック」の問題に焦点を当てました。ファイルのコピー操作がどれだけ進んでいるかの計算ではありません。
Mitch Haile 2014年

73

スピナーの方法にも興味があるかもしれません

バッシュでスピナーを実行できますか?

承知しました!

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

ループが繰り返されるたびに、sp文字列の次の文字が表示され、最後に到達すると折り返されます。(iは表示する現在の文字の位置で、$ {#sp}はsp文字列の長さです)。

\ b文字列は「バックスペース」文字に置き換えられます。または、\ rを使用して行の先頭に戻ることもできます。

速度を遅くしたい場合は、ループの内部(printfの後)にスリープコマンドを配置します。

同等のPOSIXは次のようになります。

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

多くの作業を行うループが既にある場合は、各反復の開始時に次の関数を呼び出してスピナーを更新できます。

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
はるかに短いバージョン、完全に移植可能*:while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*:sleep小数ではなく整数が必要になる場合があります)
Adam Katz '19

1
@Daenyth。ありがとう。親切に、前のコードを使用して進行状況を監視するために必要なコマンドをどこで呼び出す必要がありますか?
goro

@goro:some_work ...上記の行。POSIX準拠を中心とした- -見つけることができるこの便利な答えに構築され、アダム・カッツの役に立つコメントは、より詳細な議論こちら
mklement0 2016

@AdamKatz:役立つ、ポータブル、簡単だが、Daenythのアプローチにスピナーを一致させるために基づいていなければならない\bのではなく\r、それは非常に行の先頭にそうでないのみ機能しますと、:while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done-または、スピナーの後ろにカーソルを表示する場合望ましくない:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0 2016

1
@kaushal – Ctrl + Cで手動で停止します。あなたがバックグラウンドに仕事を持っている場合は、そのPIDを(保存することができますjob=$!)してから実行してwhile kill -0 $job 2>/dev/null;do …、例えば:sleep 15 & job=$!; while kill -0 $job 2>/dev/null; do for s in / - \\ \|; do printf "\r$s"; sleep .1; done; done
アダム・カッツ

48

一部の投稿では、コマンドの進行状況を表示する方法が示されています。それを計算するためには、どれだけ進歩したかを見る必要があります。BSDシステムでは、dd(1)などの一部のコマンドがSIGINFOシグナルを受け入れ、その進行状況を報告します。Linuxシステムでは、一部のコマンドはと同様に応答しSIGUSR1ます。この機能が利用可能な場合、入力をパイプすることができますdd処理して、処理されたバイト数を監視ます。

または、を使用lsofして、ファイルの読み取りポインターのオフセットを取得し、進行状況を計算することもできます。指定したプロセスまたはファイルの処理の進行状況を表示するコマンドpmonitorを作成しました。これを使用すると、次のようなことができます。

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

以前のバージョンのLinuxおよびFreeBSDシェルスクリプトが私のブログに掲載さています。


これは私がいつもPVを通じてパイプ物事を忘れて、素晴らしいです:-)私は「STAT」コマンドは、このスクリプトの少し異なる、私の(Linux)のバージョンうまくいくと思う:gist.github.com/unhammer/b0ab6a6aa8e1eeaf236bを
unhammer

素晴らしい投稿awkです。ゲームに参加するときはいつでもそれを愛してください!
ShellFish、2015年

これは素晴らしい!素晴らしいスクリプトをありがとう!
thebeagle 2016

47

先日書いた簡単なプログレスバー機能を手に入れました:

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

または、
https://github.com/fearside/ProgressBar/


1.2.1.1の下の行を説明できますか?_fillおよび_empty変数を使用してsed置換を実行していますか?よくわかりません。
Chirag 2017

sedを使用する代わりに、bashの内部「サブストリング置換」を使用します。これは簡単な作業なので、この種の作業にはbashの内部関数を使用することを好みます。コードも見栄えが良くなりました。:-)ここtldp.org/LDP/abs/html/string-manipulation.htmlを確認し、部分文字列の置換を検索してください。
恐怖の

また、$ {_ fill}は$ {_ done}個のスペースとして割り当てられます。これは美しいです。素晴らしい仕事人。私は間違いなくすべてのbashスクリプトでこれを使用するつもりです
Chirag

素晴らしい仕事@fearside!速度を向上させるために、_progressが最後の値から変更されていないときにスキップするように少し調整しました。github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

甘い。長方形でダッシュを変更すると、よりプロフェッショナルな外観になります。printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
メフディLAMRANI

44

Linuxコマンドpvを使用します。

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

ストリームの途中にある場合はサイズがわかりませんが、速度と合計が得られ、そこから所要時間を把握してフィードバックが得られるため、ハングしていないことがわかります。


32

選択した答えよりもセクシーなものを探していたので、自分のスクリプトも選びました。

プレビュー

進行中のprogress-bar.sh

ソース

私はそれを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
}

使用法

 progress-bar 100

1
これがプロセスの長さがわからない一部の処理にどのように統合されるのか理解できません。たとえばファイルの解凍など、プロセスが早く終了した場合にプログレスバーを停止する方法。
2018

私は使用法をする必要があると思いますprogress-bar 100
ジラリウム

確かに魅力的な進歩。どのようにして、sshを介してリモートサーバーでの長時間のアクションを処理する機能に関連付けることができますか?つまり、リモートサーバーでのアップグレード(たとえば)のタイミングを測定する方法はありますか。
顔の見えない2018

1
@facelessそれはあなたが時間を提供し、それがカウントダウンこのコードの範囲ではありません
エドゥアール・ロペス

1
@Fusionは、モダンシェルに安全なユニコード文字(U + 2587 LOWER SEVEN EIGHTHS BLOCK)です。あなたの環境で試してみてください
エドゥアールロペス

18

GNU tarには、シンプルなプログレスバーの機能を提供する便利なオプションがあります。

(...)別の使用可能なチェックポイントアクションは「ドット」(または「。」)です。次のように、標準のリストストリームに単一のドットを印刷するようにtarに指示します。

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

同じ効果は次の方法でも得られます。

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

+1で最も簡単なアプローチ!ドットが印刷されない場合は、数を減らしてみてください--checkpoint=.10。で抽出する場合にも効果的tar -xzです。
Noam Manos

13

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

類似するものは何も見たことがなく、ここでのすべてのカスタム関数はレンダリングのみに焦点を当てているようです...この質問は簡単ではないため、以下のステップごとの説明を含む非常に単純なPOSIX準拠のソリューション。

TL; DR

プログレスバーのレンダリングはとても簡単です。どれだけレンダリングするかを見積もることは別の問題です。これは、プログレスバーをレンダリング(アニメーション)する方法です。この例をファイルにコピーして貼り付け、実行できます。

#!/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} -1から20までの値
  • echo -n -最後に改行なしで印刷
  • echo -e -印刷中に特殊文字を解釈する
  • "\r" -改行、行の先頭に戻るための特別な文字

任意の速度で任意のコンテンツをレンダリングできるため、この方法は非常に一般的です。たとえば、ばかげた映画の「ハッキング」の視覚化によく使用され、冗談ではありません。

完全な答え

問題の核心は、どのように$i値を決定するか、つまり、表示する進行状況バーの量です。上記の例でforは、原理を説明するためにループでインクリメントさせていますが、実際のアプリケーションでは無限ループを使用し、$i各反復で変数を計算します。この計算を行うには、次の成分が必要です。

  1. どれだけの作業が必要ですか
  2. これまでにどれだけの作業が行われたか

cpソースファイルのサイズとターゲットファイルのサイズが必要な場合:

#!/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 -ファイルの統計を確認する
  • -c -フォーマットされた値を返す
  • %s - 全体の大きさ

ファイルの解凍などの操作の場合、ソースサイズの計算は少し難しくなりますが、非圧縮ファイルのサイズを取得するのと同じくらい簡単です。

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
  • gzip -l -zipアーカイブに関する情報を表示する
  • tail -n1 -下から1行で作業
  • tr -s ' ' -複数のスペースを1つに変換します(それらを絞ります)
  • cut -d' ' -f3 -3番目のスペース区切りの列を切り取ります

しかし、ここに問題の核心があります。このソリューションは、一般的ではありません。実際の進行状況のすべての計算は、視覚化しようとしているドメインに密接に結びついています。それは、単一のファイル操作、タイマーのカウントダウン、ディレクトリ内のファイル数の増加、複数のファイルに対する操作などです。したがって、再利用できません。再利用可能な唯一の部分は、進行状況バーのレンダリングです。それを再利用するには、抽象化してファイル(例:)に保存し、/usr/lib/progress_bar.shドメイン固有の入力値を計算する関数を定義する必要があります。これは一般化されたコードがどのように見えるかです($BAR人々がそれを求めていたので私も動的にしました、残りは今までに明確になるはずです):

#!/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 -所定の形式で印刷するためのビルトイン
  • printf '%50s' -何も印刷せず、50スペースで埋める
  • tr ' ' '#' -すべてのスペースをハッシュ記号に変換します

そして、これはあなたがそれを使う方法です:

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

明らかに、関数にラップしたり、パイプストリームで動作するように書き直したり、他の言語に書き直したりすることができます。


1
最も単純なものを求めている人のために、私はcprnの最初の答えで私のものを作りました。:それはバーを描画するために、いくつかの愚かな比例ルールを使用機能で非常にシンプルなプログレスバーのpastebin.com/9imhRLYX
YCN-


9

APTスタイルの進行状況バー(通常の出力を中断しません)

ここに画像の説明を入力してください

編集:更新されたバージョンについては、私のgithubページを確認してください

この質問への回答に満足できませんでした。私が個人的に探していたのは、APTで見られるような、洗練された進行状況バーです。

APTのCソースコードを確認し、bashに相当する独自のコードを作成することにしました。

この進行状況バーは端末の下部にうまく表示され、端末に送信される出力を妨げることはありません。

バーは現在100文字幅に固定されていることに注意してください。端末のサイズに合わせてスケーリングしたい場合は、これもかなり簡単に実行できます(私のgithubページの更新バージョンはこれをうまく処理します)。

ここにスクリプトを投稿します。使用例:

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

スクリプト(代わりに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

これにより、コマンドがまだ実行中であることを視覚化できます。

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

これにより、バックグラウンドで実行され、「。」をエコーする無限のwhileループが作成されます。毎秒。これは.シェルに表示されます。実行tarしたいコマンドまたは任意のAコマンドを。そのコマンドが終了すると、次に実行するときに殺すです-バックグラウンドで実行されている最後のジョブ無限whileループを


実行中に別のジョブがバックグラウンドで開始され、進行ループの代わりに強制終了される可能性はありませんか?
Centimane

私は、これをスクリプトに入れるという考えがあると思うので、これはそのスクリプトの出口をトラップするだけです。
イグアナナウ

1
私はこのコマンドが大好きで、自分のファイルで使用しています。仕組みがよくわからないので少し不安です。1行目と3行目は理解しやすいですが、それでもわかりません。私はこれが古い答えであることを知っていますが、プログラミングで初心者向けに別の説明を得る方法はありますか
フェリペ

1
これは唯一の真の答えです。他の人は、何も意味せず、実際の1回限りの追跡不可能な(ほとんどすべての)プログラムには役に立たないScripting 101おもちゃの進行状況バーです。ありがとうございました。
bekce

@ Felipe、whileループはバックグラウンドプロセスです。$!最初のトラップでは、そのバックグラウンドプロセスのプロセスIDをキャプチャし、現在のプロセスまたは親プロセスが終了した場合にバックグラウンドプロセスも停止し、ハングしたままにならないようにします。killステートメントは、長いコマンドが終了したときにバックグラウンドプロセスを終了するだけです。
フロイド

7

これはどのように見えるかです

ファイルをアップロードする

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

ジョブが完了するのを待っています

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

それを実装する単純な関数

スクリプトにコピーして貼り付けるだけです。それは動作するために他に何も必要としません。

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

使用例

ここでは、ファイルをアップロードし、反復ごとにプログレスバーを再描画します。最大値と現在値の2つの値を取得できる限り、実際に実行されるジョブは問題ではありません。

以下の例では、最大値はでfile_sizeあり、現在の値は何らかの関数によって提供され、と呼ばれ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"

すっきりとしたシンプルな機能。どうもありがとう!
Andreas Kraft

これは私が探しているものです!
どうも

4

ほとんどのunixコマンドは、これを実行できるような直接的なフィードバックを提供しません。いくつかは、使用できるstdoutまたはstderrに出力を提供します。

tarなどの場合は、-vスイッチを使用して、読み取った各行の小さなアニメーションを更新するプログラムに出力をパイプできます。tarがファイルのリストを書き出すと、それが解明され、プログラムはアニメーションを更新できます。完了率を計算するには、ファイル数を把握し、行数を数える必要があります。

私が知る限り、cpはこの種の出力を提供しません。cpの進行状況を監視するには、ソースファイルと宛先ファイルを監視し、宛先のサイズを監視する必要があります。ファイルサイズを取得するために、stat(2)システムコールを使用して小さなcプログラムを書くことができます。これは、ソースのサイズを読み取ってから、宛先ファイルをポーリングし、現在までに書き込まれたファイルのサイズに基づいて%完了バーを更新します。


4

私のソリューションは、現在圧縮解除されて書き込まれているtarballのパーセンテージを表示します。2GBのルートファイルシステムイメージを書き込むときにこれを使用します。これらのことについては、プログレスバーが本当に必要です。私がやっていることは gzip --list、圧縮されていないtarballの合計サイズを取得するためにすることです。それから、ファイルを100の部分に分割するために必要なブロッキング係数を計算します。最後に、各ブロックのチェックポイントメッセージを出力します。2GBファイルの場合、これは約10MBのブロックを与えます。それが大きすぎる場合は、BLOCKING_FACTORを10または100で割ることができますが、かなりの出力をパーセンテージで出力することは困難です。

あなたがBashを使用していると仮定すると、次のシェル関数を使用できます

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

素晴らしい解決策ですが、ディレクトリを圧縮したい場合はどうしますか?
Samir Sadek、2018年

4

まず第一に、バーは唯一のパイプ進捗メーターではありません。もう1つは、pv(パイプビューア)です。

次に、barとpvは、たとえば次のように使用できます。

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

あるいは:

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

たとえば、copy file1 file2のように、引数で指定されたファイルを操作するコマンドでbarとpvを使用したい場合の1つの便利なトリックは、プロセス置換を使用することです。

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

プロセスの置換は一時的なfifoパイプファイル/ dev / fd /を作成し、このパイプを介して実行されたプロセス(括弧内)からstdoutを接続し、コピーは通常のファイルと同じように認識します(1つの例外を除いて、読み取ることしかできません)転送)。

更新:

barコマンド自体もコピーが可能です。男性バーの後:

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

しかし、私の意見では、プロセス置換はそれを行うためのより一般的な方法です。cpプログラム自体を使用します。


3

私が使用することを好む、ダイアログ--gauge PARAM。.debパッケージのインストールや、多くのディストリビューションの他の基本的な構成で非常に頻繁に使用されます。したがって、ホイールを再発明する必要はありません...もう一度

1〜100の@stdinのint値を入力するだけです。基本的で愚かな1つの例:

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

私は調理のためにこの/ bin / Waitファイル(chmod u + x permsを含む)を持っています: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"

だから私は置くことができます:

Wait "34 min" "warm up the oven"

または

Wait "dec 31" "happy new year"


2

私にとって最も使いやすく、これまでのところ最高に見えるのは、コマンドpvまたはbar誰かがすでに書いたようなものです

例:ドライブ全体のバックアップを作成する必要がある dd

通常使用します dd if="$input_drive_path" of="$output_file_path"

pvあなたはこのようにそれを行うことができます。

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

進行状況は次のように直接進みますSTDOUT

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

終わったらまとめが出てきます

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

タイマーのカウントダウン、テキストファイル内の位置、アプリのインストール、ランタイムのセットアップなど、さまざまなプロセスの進行状況を使用pvまたはbar視覚化できますか?
cprn

2

多くの回答は、印刷するための独自のコマンドの記述について説明しています'\r' + $some_sort_of_progress_msg。問題は、これらの更新を毎秒何百回も出力すると、プロセスが遅くなることです。

ただし、プロセスのいずれかが出力を生成する場合(たとえば7z a -r newZipFile myFolder、圧縮時に各ファイル名を出力する場合)は、より単純で、高速で、痛みがなく、カスタマイズ可能なソリューションが存在します。

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

ヘルプ:tqdm -h。より多くのオプションを使用した例:

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

おまけとして、tqdmイテラブルをPythonコードでラップすることもできます。

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


「より多くのオプション」を使用した例は機能しないと思います。渡すように見えるtqdmにSTDOUTをwc -lパイプを通して。あなたはおそらくそれを脱出したいでしょう。
cprn

1
@cprn tqdmSTDERR、入力STDINをにパイプしているときに進行状況を表示しSTDOUTます。この場合wc -ltqdm含まれていないかのように同じ入力を受け取るはずです。
casper.dcl

ああ、今は理にかなっています。説明していただきありがとうございます。
cprn

2

エドゥアールロペスの作品に基づいて、画面サイズにかかわらず、画面のサイズに合うプログレスバーを作成しました。見てみな。

ここに画像の説明を入力してください

また、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

楽しい



1

これは、gnome zenityを使用する場合にのみ適用されます。Zenityは、bashスクリプトへの優れたネイティブインターフェイスを提供します。https//help.gnome.org/users/zenity/stable/

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

シェルスクリプトで繰り返し文字の文字列を作成するからの回答を文字繰り返しに使用しました。進行状況バーを表示する必要があるスクリプト用に2つの比較的小さなbashバージョンがあります(たとえば、多くのファイルを通過するループですが、大きなtarファイルやコピー操作には役立ちません)。高速なものは2つの関数で構成され、1つはバー表示用の文字列を準備するためのものです。

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

もう1つは進行状況バーを表示します。

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
}

次のように使用できます。

preparebar 50 "#"

これは、50の「#」文字でbarの文字列を準備することを意味し、その後:

progressbar 35 80

35/80比に対応する「#」文字の数が表示されます。

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

functionは、ユーザー(または他のプログラム)が改行を印刷するまで、同じ行にバーを繰り返し表示することに注意してください。最初のパラメーターとして-1を指定すると、バーは消去されます。

progressbar -1 80

遅いバージョンはすべて1つの関数に含まれています。

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
}

そしてそれは(上記と同じ例)として使用できます:

progressbar 35 80 50

stderrにプログレスバーが必要な場合>&2は、各printfコマンドの最後に追加してください。


1

アクティビティの進行状況を示すには、次のコマンドを試してください。

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;

または

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;

または

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;

または

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;

whileループ内でフラグ/変数を使用して、進行状況の値/範囲を確認および表示できます。


1

上記の提案を使用して、自分の進行状況バーを実装することにしました。

#!/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
いいね!それは私が行を変更しなければならなかった作業を取得するためpercent_none="$(( 100 - "$percent_done" ))"percent_none="$(( 100 - $percent_done))"
セルジオ

0

私は組み込みシステム用の純粋なシェルバージョンを利用しました。

  • / usr / bin / ddのSIGUSR1シグナル処理機能。

    基本的に、「kill SIGUSR1 $(pid_of_running_dd_process)」を送信すると、スループット速度と転送量の概要が出力されます。

  • ddをバックグラウンド化し、定期的に更新をクエリし、昔ながらのftpクライアントのようなハッシュティックを生成する。

  • / dev / stdoutをscpのようなstdoutに対応していないプログラムの宛先として使用する

最終結果では、任意のファイル転送操作を実行して、Xバイトごとにハッシュマークを取得するだけの古いFTPの「ハッシュ」出力のような進行状況の更新を取得できます。

これはほとんどプロダクション品質のコードではありませんが、アイデアは理解できます。かわいいと思います。

それだけの価値がある場合、実際のバイト数はハッシュの数に正しく反映されない可能性があります。丸めの問題に応じて、1つ以上ある場合があります。これはテストスクリプトの一部として使用しないでください。見栄えがいいだけです。そして、はい、これはひどく非効率的であることを認識しています。これはシェルスクリプトであり、謝罪しません。

最後にwget、scp、tftpを使用した例。データを放出するものなら何でも動作するはずです。stdoutに対応していないプログラムには、必ず/ dev / stdoutを使用してください。

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

例:

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

まともなアイデア、事前にファイルサイズがある限り、この方法でpvよりも付加価値を提供できますが、盲目的に信号を送るのpidof ddは怖いです。

「#PID処理がおかしい」でそれを呼びだそうとした
シンセサイザー

キャプチャ$!ddて待機することができ[[ -e /proc/${DD_PID} ]]ます。

0

時間の進行状況バーを表示する必要がある場合(表示時間を事前に知ることにより)、次のようにPythonを使用できます。

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

次に、Pythonスクリプトをとして保存したとprogressbar.pyすると、次のコマンドを実行することにより、bashスクリプトからプログレスバーを表示することができます。

python progressbar.py 10 50

進行状況バーのサイズの50文字と「実行中」の10秒が表示されます。


0

私は恐れ側から提供された答えに基づいています

これは、Oracleデータベースに接続して、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

}

バーの数を1〜10のスケールで描画する関数を作成できます。

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;

出力:

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

注:255の代わりに1を入力すると、標準入力を監視します... 2を標準出力にします(ただし、「tot」を予測される出力ファイルサイズに設定するようにソースを変更する必要があります)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.