* NIXのbashまたはその他のシェルでスクリプトを作成する場合、数秒以上かかるコマンドを実行している間、進行状況バーが必要です。
たとえば、大きなファイルをコピーしたり、大きなtarファイルを開いたりします。
シェルスクリプトに進行状況バーを追加するには、どのような方法をお勧めしますか?
* NIXのbashまたはその他のシェルでスクリプトを作成する場合、数秒以上かかるコマンドを実行している間、進行状況バーが必要です。
たとえば、大きなファイルをコピーしたり、大きなtarファイルを開いたりします。
シェルスクリプトに進行状況バーを追加するには、どのような方法をお勧めしますか?
回答:
これを実装するには、行を上書きします。\r
何も書かずに行の先頭に戻るために使用します\n
端末にことます。
書く \n
あなたが行を進めるために行われているとき。
使用echo -ne
する:
\n
ず\r
ます。ここにデモがあります:
echo -ne '##### (33%)\r'
sleep 1
echo -ne '############# (66%)\r'
sleep 1
echo -ne '####################### (100%)\r'
echo -ne '\n'
以下のコメントで、長い行から始めて短い行を書きたい場合、pukはこの「失敗」について言及します。この場合、長い行の長さ(スペースなど)を上書きする必要があります。
printf
代わりにを使用することですecho
。
printf "#### (50%%)\r"
必要があります。、単一引用符では機能せず、パーセント記号をエスケープする必要があります。
承知しました!
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
while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done
(*:sleep
小数ではなく整数が必要になる場合があります)
some_work ...
上記の行。POSIX準拠を中心とした- -見つけることができるこの便利な答えに構築され、アダム・カッツの役に立つコメントは、より詳細な議論こちら。
\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
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
一部の投稿では、コマンドの進行状況を表示する方法が示されています。それを計算するためには、どれだけ進歩したかを見る必要があります。BSDシステムでは、dd(1)などの一部のコマンドがSIGINFO
シグナルを受け入れ、その進行状況を報告します。Linuxシステムでは、一部のコマンドはと同様に応答しSIGUSR1
ます。この機能が利用可能な場合、入力をパイプすることができますdd
処理して、処理されたバイト数を監視ます。
または、を使用lsof
して、ファイルの読み取りポインターのオフセットを取得し、進行状況を計算することもできます。指定したプロセスまたはファイルの処理の進行状況を表示するコマンドpmonitorを作成しました。これを使用すると、次のようなことができます。
$ pmonitor -c gzip
/home/dds/data/mysql-2015-04-01.sql.gz 58.06%
awk
です。ゲームに参加するときはいつでもそれを愛してください!
先日書いた簡単なプログレスバー機能を手に入れました:
#!/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'
printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Linuxコマンドpvを使用します。
ストリームの途中にある場合はサイズがわかりませんが、速度と合計が得られ、そこから所要時間を把握してフィードバックが得られるため、ハングしていないことがわかります。
選択した答えよりもセクシーなものを探していたので、自分のスクリプトも選びました。
私はそれを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
progress-bar 100
GNU tarには、シンプルなプログレスバーの機能を提供する便利なオプションがあります。
(...)別の使用可能なチェックポイントアクションは「ドット」(または「。」)です。次のように、標準のリストストリームに単一のドットを印刷するようにtarに指示します。
$ tar -c --checkpoint=1000 --checkpoint-action=dot /var
...
同じ効果は次の方法でも得られます。
$ tar -c --checkpoint=.1000 /var
--checkpoint=.10
。で抽出する場合にも効果的tar -xz
です。
類似するものは何も見たことがなく、ここでのすべてのカスタム関数はレンダリングのみに焦点を当てているようです...この質問は簡単ではないため、以下のステップごとの説明を含む非常に単純なPOSIX準拠のソリューション。
プログレスバーのレンダリングはとても簡単です。どれだけレンダリングするかを見積もることは別の問題です。これは、プログレスバーをレンダリング(アニメーション)する方法です。この例をファイルにコピーして貼り付け、実行できます。
#!/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
各反復で変数を計算します。この計算を行うには、次の成分が必要です。
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
明らかに、関数にラップしたり、パイプストリームで動作するように書き直したり、他の言語に書き直したりすることができます。
編集:更新されたバージョンについては、私の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}"
}
これにより、コマンドがまだ実行中であることを視覚化できます。
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ループを。
ファイルをアップロードする
[##################################################] 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"
ほとんどのunixコマンドは、これを実行できるような直接的なフィードバックを提供しません。いくつかは、使用できるstdoutまたはstderrに出力を提供します。
tarなどの場合は、-vスイッチを使用して、読み取った各行の小さなアニメーションを更新するプログラムに出力をパイプできます。tarがファイルのリストを書き出すと、それが解明され、プログラムはアニメーションを更新できます。完了率を計算するには、ファイル数を把握し、行数を数える必要があります。
私が知る限り、cpはこの種の出力を提供しません。cpの進行状況を監視するには、ソースファイルと宛先ファイルを監視し、宛先のサイズを監視する必要があります。ファイルサイズを取得するために、stat(2)システムコールを使用して小さなcプログラムを書くことができます。これは、ソースのサイズを読み取ってから、宛先ファイルをポーリングし、現在までに書き込まれたファイルのサイズに基づいて%完了バーを更新します。
私のソリューションは、現在圧縮解除されて書き込まれている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}
}
まず第一に、バーは唯一のパイプ進捗メーターではありません。もう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プログラム自体を使用します。
私が使用することを好む、ダイアログで--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"
私にとって最も使いやすく、これまでのところ最高に見えるのは、コマンド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
視覚化できますか?
多くの回答は、印刷するための独自のコマンドの記述について説明しています'\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コードでラップすることもできます。
tqdm
にSTDOUTをwc -l
パイプを通して。あなたはおそらくそれを脱出したいでしょう。
tqdm
はSTDERR
、入力STDIN
をにパイプしているときに進行状況を表示しSTDOUT
ます。この場合wc -l
、tqdm
含まれていないかのように同じ入力を受け取るはずです。
エドゥアールロペスの作品に基づいて、画面サイズにかかわらず、画面のサイズに合うプログレスバーを作成しました。見てみな。
#!/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
楽しい
これは、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
シェルスクリプトで繰り返し文字の文字列を作成するからの回答を文字繰り返しに使用しました。進行状況バーを表示する必要があるスクリプト用に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コマンドの最後に追加してください。
アクティビティの進行状況を示すには、次のコマンドを試してください。
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ループ内でフラグ/変数を使用して、進行状況の値/範囲を確認および表示できます。
上記の提案を使用して、自分の進行状況バーを実装することにしました。
#!/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 "$@"
percent_none="$(( 100 - "$percent_done" ))"
にpercent_none="$(( 100 - $percent_done))"
私は組み込みシステム用の純粋なシェルバージョンを利用しました。
/ 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
時間の進行状況バーを表示する必要がある場合(表示時間を事前に知ることにより)、次のように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
秒が表示されます。
私は恐れ側から提供された答えに基づいています
これは、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'
#!/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
#!/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」を予測される出力ファイルサイズに設定するようにソースを変更する必要があります)。