शेल स्क्रिप्ट में प्रगति पट्टी कैसे जोड़ें?


413

जब * NIX में बैश या किसी अन्य शेल में स्क्रिप्टिंग करते हैं, तो कुछ सेकंड से अधिक समय लगने वाले कमांड को चलाते समय एक प्रगति बार की जरूरत होती है।

उदाहरण के लिए, एक बड़ी फ़ाइल की प्रतिलिपि बनाना, एक बड़ी टार फ़ाइल खोलना।

शेल स्क्रिप्ट्स में प्रगति सलाखों को जोड़ने के लिए आप किन तरीकों की सलाह देते हैं?


नियंत्रण तर्क के उदाहरण के लिए stackoverflow.com/questions/12498304/… को भी देखें (किसी कार्य को पृष्ठभूमि दें और जब तक यह पूरा न हो जाए)।
20

1
स्क्रिप्टिंग के समय हम अक्सर उपयोगी पाते हैं आवश्यकताओं का एक समूह है। लॉगिंग, प्रगति, रंग, फैंसी आउटपुट आदि प्रदर्शित करना ... मैंने हमेशा महसूस किया है कि किसी प्रकार की सरल स्क्रिप्टिंग रूपरेखा होनी चाहिए। अंत में मैंने एक को लागू करने का फैसला किया है क्योंकि मुझे कोई नहीं मिला। आपको यह मददगार लग सकता है। यह शुद्ध बैश में है, मेरा मतलब बस बैश है। github.com/SumuduLansakara/JustBash
Sumudu

क्या इसे 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'

नीचे एक टिप्पणी में, पुक इस "विफल" का उल्लेख करता है यदि आप एक लंबी रेखा से शुरू करते हैं और फिर एक छोटी रेखा लिखना चाहते हैं: इस मामले में, आपको लंबी रेखा (जैसे, रिक्त स्थान के साथ) की लंबाई को अधिलेखित करने की आवश्यकता होगी।


23
इको मैन पेज के अनुसार (कम से कम MacOS X पर) sh / bash अपने स्वयं के अंतर्निहित इको कमांड का उपयोग करते हैं जो "-n" स्वीकार नहीं करता है ... इसलिए उसी चीज़ को पूरा करने के लिए जिसे आपको \ r डालने की आवश्यकता है स्ट्रिंग के अंत में \ c , जस्ट \ r के
जस्टिन जेनकिंस

51
इसका उत्पादन करने का पोर्टेबल तरीका printfइसके बजाय उपयोग करना है echo
जेन्स

9
प्रिंटफ के लिए हमें इस प्रारूप का उपयोग printf "#### (50%%)\r"करना होगा : यह सिंगल कोट्स के साथ काम नहीं करेगा और प्रतिशत चिन्ह को बचाना होगा।
nurettin

7
मुझे यह स्वीकार नहीं है - एक "के लिए upvotes का ढेर और" मैं अनुमान लगाता हूं कि यह ऑपरेशन अज्ञात हार्डवेयर पर कितना समय लगेगा "हैक? pv सही उत्तर है IMO (लेकिन बार भी करेगा)
Stephen

19
सवाल था "मैं फ़ाइलों को कैसे कॉपी करूं" प्रगति की नकल करते हैं। मैंने "ग्राफिक्स" समस्या पर ध्यान केंद्रित किया, न कि गणना की कि फाइल कॉपी ऑपरेशन कितनी दूर है।
मिच हेल

73

आपको स्पिनर करने के तरीके में भी रुचि हो सकती है :

क्या मैं बैश में स्पिनर कर सकता हूं?

ज़रूर!

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

जितनी बार लूप पुनरावृत्त होता है, वह अगले स्ट्रिंग में अगले वर्ण को प्रदर्शित करता है, क्योंकि यह अंत तक पहुंचता है। (मैं वर्तमान चरित्र की स्थिति प्रदर्शित करने के लिए और $ {# sp} sp स्ट्रिंग की लंबाई है)।

\ B स्ट्रिंग को 'बैकस्पेस' वर्ण द्वारा प्रतिस्थापित किया जाता है। वैकल्पिक रूप से, आप लाइन की शुरुआत में वापस जाने के लिए \ r के साथ खेल सकते हैं।

यदि आप इसे धीमा करना चाहते हैं, तो लूप के अंदर स्लीप कमांड डालें (प्रिंटफ के बाद)।

एक 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दशमलव के बजाय ints की आवश्यकता हो सकती है)
एडम काट्ज

1
@Daenyth। धन्यवाद। कृपया कहां से हमें उस आदेश को कॉल करना चाहिए जिसे हमें देखने की आवश्यकता है जो पिछले कोड का उपयोग करके प्रगति कर रहा है?
गोरो

@ कॉरगो: some_work ...ऊपर की पंक्ति में; अधिक विस्तृत चर्चा जो इस सहायक उत्तर और एडम काटज़ की सहायक टिप्पणी पर आधारित है - पोसिक्स अनुपालन पर ध्यान देने के साथ - यहां पाया जा सकता है
mklement0

@AdamKatz: यह एक सहायक, पोर्टेबल सरलीकरण है, लेकिन डैनिथ के दृष्टिकोण से मिलान करने के लिए स्पिनर के \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

1
@ कौशल - Ctrl + C इसे मैन्युअल रूप से बंद कर देगा। यदि आपके पास एक पृष्ठभूमि वाली नौकरी है, तो आप इसके पीआईडी ​​( 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

कुछ पोस्टों में दिखाया गया है कि कमांड की प्रगति को कैसे प्रदर्शित किया जाए। इसकी गणना करने के लिए, आपको यह देखना होगा कि आपने कितनी प्रगति की है। बीएसडी सिस्टम पर कुछ कमांड्स, जैसे dd (1), एक SIGINFOसिग्नल को स्वीकार करते हैं , और उनकी प्रगति की रिपोर्ट करेंगे। लिनक्स सिस्टम पर कुछ कमांड इसी तरह का जवाब देंगे SIGUSR1। यदि यह सुविधा उपलब्ध है, तो आप अपने इनपुट के माध्यम से पाइप कर सकते हैंdd संसाधित किए गए बाइट्स की संख्या की निगरानी के लिए हैं।

वैकल्पिक रूप से, आप lsofफ़ाइल के रीड पॉइंटर की ऑफसेट प्राप्त करने के लिए उपयोग कर सकते हैं , और इस तरह प्रगति की गणना कर सकते हैं। मैंने एक कमांड लिखा है, जिसका नाम pmonitor है , जो एक निर्दिष्ट प्रक्रिया या फ़ाइल को संसाधित करने की प्रगति प्रदर्शित करता है। इसके साथ आप चीजें कर सकते हैं, जैसे कि निम्नलिखित।

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

मेरे ब्लॉग पर लिनक्स और फ्रीबीएसडी शेल स्क्रिप्ट का एक पुराना संस्करण दिखाई देता है ।


यह बहुत बढ़िया है, मैं हमेशा pv :-) के माध्यम से चीजों को पाइप करना भूल जाता हूं। मुझे लगता है कि मेरी "स्टेट" कमांड थोड़ी अलग तरह से काम करती है, इस स्क्रिप्ट का मेरा (लिनक्स) संस्करण: gist.github.com/unhammer/b0ab6a6aa8aeeef236b
unhammer

महान पोस्ट, हमेशा इसे प्यार करता हूँ जब awkखेल में है!
शेलफिश

यह भी खूब रही! कमाल की स्क्रिप्ट के लिए धन्यवाद!
thebeagle

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 प्रतिस्थापन कर रहे हैं? मैं उलझन में हूं।
चिराग

सीड का उपयोग करने के बजाय, बैश आंतरिक "प्रतिस्थापन प्रतिस्थापन" का उपयोग करके im, क्योंकि यह एक आसान काम है, मैं इस तरह के काम के लिए बैश के आंतरिक कार्यों का उपयोग करना पसंद करता हूं। कोड अच्छा लग रहा है। :-) यहाँ देखें tldp.org/LDP/abs/html/string-manipulation.html और प्रतिस्थापन प्रतिस्थापन के लिए खोज करें।
डर

और $ {_भरण} को रिक्त स्थान की संख्या के रूप में $ {_प्रयुक्त} सौंपा गया है। ये सुन्दर है। महान नौकरी वाला। मैं निश्चित रूप से मेरी सभी बैश लिपियों में इसका उपयोग करने जा रहा हूं
चिराग

महान काम @fearside! जब गति में सुधार करने के लिए _progress अंतिम मूल्य से नहीं बदला, तो मैंने छोड़ने के लिए थोड़ा ट्वीक किया। github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

मिठाई। आयत द्वारा पानी का छींटा बदलना इसे और अधिक पेशेवर रूप देता है और महसूस करता है:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
मेहदी लमरानी

44

लिनक्स कमांड pv का उपयोग करें:

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

यदि यह धारा के बीच में है, तो इसका आकार नहीं पता है, लेकिन यह एक गति और कुल देता है और वहां से आप यह पता लगा सकते हैं कि इसे कितना समय लेना चाहिए और प्रतिक्रिया प्राप्त करनी चाहिए ताकि आपको पता चले कि यह लटका नहीं है।


32

मैं चयनित उत्तर की तुलना में अधिक सेक्सी लग रही थी, इसलिए मैंने अपनी स्क्रिप्ट बनाई।

पूर्वावलोकन

प्रगति- कार्रवाई में

स्रोत

मैंने इसे गीठूब पर रख दिया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
मुझे समझ में नहीं आता कि यह कैसे कुछ प्रसंस्करण में एकीकृत होता है जहां प्रक्रिया की लंबाई ज्ञात नहीं है। यदि मेरी प्रक्रिया पहले समाप्त हो गई है, तो प्रगति पट्टी को कैसे रोकें, जैसे किसी फ़ाइल को खोलना।
जनवरी

मुझे लगता है कि उपयोग होना चाहिएprogress-bar 100
jirarium

वास्तव में आकर्षक प्रगति। इसे एक फ़ंक्शन से कैसे जोड़ा जा सकता है जो ssh पर दूरस्थ सर्वर पर लंबे समय तक कार्रवाई करता है? मेरा मतलब है कि दूरस्थ सर्वर पर अपग्रेड (उदाहरण के लिए) के समय को मापना कैसे संभव है?
फेसलेस

1
@ यदि आप इस कोड के दायरे में नहीं हैं तो आप समय प्रदान करते हैं और यह नीचे गिना जाता है
arddouard Lopez

1
@ फ्यूजन यह एक यूनिकोड चरित्र है (U + 2587 LOWER SEVEN EIGHTHS BLOCK) जो आधुनिक शेल के लिए सुरक्षित होना चाहिए। इसे अपने envs पर आज़माएं
Lopez

18

GNU टार में एक उपयोगी विकल्प है जो एक साधारण प्रगति बार की कार्यक्षमता देता है।

(...) एक और उपलब्ध चेकपॉइंट कार्रवाई 'डॉट' (या '।') है। यह मानक लिस्टिंग स्ट्रीम पर सिंगल डॉट प्रिंट करने के लिए टार को निर्देश देता है, जैसे:

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

उसी प्रभाव द्वारा प्राप्त किया जा सकता है:

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

सरल दृष्टिकोण के लिए +1! यदि आप कोई डॉट्स मुद्रित नहीं देखते हैं, उदाहरण के लिए, संख्या को कम करने का प्रयास करें --checkpoint=.10। यह भी जब निकालने के साथ महान काम करता है tar -xz
नोआम मानोस

13

एक सरल विधि जो पाइपव्यू (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 कंप्लायंट स्टेप बाय स्टेप एक्सप्लेनेशन्स नीचे दिए गए हैं क्योंकि यह सवाल मामूली नहीं है।

टी एल; डॉ

प्रगति पट्टी को सौंपना बहुत आसान है। यह अनुमान लगाना कि इसका कितना रेंडर होना चाहिए, यह अलग बात है। यह है कि प्रगति पट्टी को कैसे प्रस्तुत करें (चेतन करें) - आप इस उदाहरण को किसी फ़ाइल में कॉपी और पेस्ट कर सकते हैं और इसे चला सकते हैं:

#!/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 - ज़िप संग्रह के बारे में जानकारी प्रदर्शित करें
  • tail -n1 - नीचे से 1 लाइन के साथ काम करें
  • tr -s ' ' - एक के लिए कई रिक्त स्थान का अनुवाद (उन्हें निचोड़ें)
  • 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 स्टाइल प्रगति बार (सामान्य आउटपुट को नहीं तोड़ता)

यहां छवि विवरण दर्ज करें

संपादित करें: एक अद्यतन संस्करण के लिए मेरी जाँच करें जीथूब पृष्ठ की

मैं इस सवाल पर प्रतिक्रियाओं से संतुष्ट नहीं था। मैं व्यक्तिगत रूप से जिस चीज की तलाश कर रहा था वह एक फैंसी प्रोग्रेस बार थी जैसा कि एपीटी द्वारा देखा जाता है।

मैंने एपीटी के लिए सी सोर्स कोड पर एक नज़र डाली और बैश के लिए अपने समकक्ष लिखने का फैसला किया।

यह प्रगति पट्टी टर्मिनल के निचले हिस्से में अच्छी तरह से रहेगी और टर्मिनल में भेजे गए किसी भी आउटपुट के साथ हस्तक्षेप नहीं करेगी।

कृपया ध्यान दें कि बार वर्तमान में 100 वर्णों पर विस्तृत है। यदि आप इसे टर्मिनल के आकार में स्केल करना चाहते हैं, तो यह भी पूरा करना आसान है (मेरे गीथब पृष्ठ पर अपडेट किया गया संस्करण इसे अच्छी तरह से संभालता है)।

मैं अपनी स्क्रिप्ट यहां पोस्ट करूंगा। उपयोग उदाहरण:

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

स्क्रिप्ट (मैं इसके बजाय मेरे गीथूब पर संस्करण की जोरदार सिफारिश करता हूं):

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

यह एक अनंत लूप पैदा करेगा जो पृष्ठभूमि में निष्पादित होता है और एक "echoes"। हर पल। यह .शेल में प्रदर्शित होगा । tarकमांड या कोई भी कमांड जिसे आप चाहते हैं , चलाएं । जब वह कमांड निष्पादित करना समाप्त कर देता है, तो पृष्ठभूमि में चल रहे अंतिम काम को मार दें - जो लूप के दौरान अनंत है


निष्पादन के दौरान पृष्ठभूमि में एक और काम शुरू नहीं हो सका और संभावित लूप के बजाय संभावित रूप से मारे गए?
सेंटिमेन

मुझे लगता है कि विचार यह है कि आप इसे एक स्क्रिप्ट में डाल देंगे, इसलिए यह केवल उस स्क्रिप्ट से बाहर निकलना होगा।
इगुआनाउत

1
मुझे यह कमांड पसंद है, मैं इसे अपनी फाइलों में इस्तेमाल कर रहा हूं। मैं बस थोड़ा असहज हूं क्योंकि मुझे समझ नहीं आ रहा है कि यह कैसे काम करता है। पहली और तीसरी पंक्ति को समझना आसान है, लेकिन मुझे अभी भी यकीन नहीं है। मुझे पता है कि यह एक पुराना उत्तर है, लेकिन क्या कोई ऐसा तरीका है जिससे मैं प्रोग्रामिंग में नए-नए विषयों की ओर एक अलग व्याख्या प्राप्त कर सकता हूं
फेलिप

1
यह केवल सही उत्तर है, जहां अन्य केवल 101 टॉय प्रगति प्रगति वाली स्क्रिप्टिंग कर रहे हैं, जिसका मतलब कुछ भी नहीं है और वास्तविक, वन-ऑफ, अनट्रैकेबल (लगभग सभी) कार्यक्रमों के लिए कोई उपयोग नहीं है। धन्यवाद।
bekce

@ फेलिप, द लूप एक बैकग्राउंड प्रोसेस है। द $! पहले ट्रैप में उस बैकग्राउंड प्रोसेस के प्रोसेस आईडी को कैप्चर किया जाता है और यह सुनिश्चित करता है कि अगर करंट / पैरेंट प्रोसेस खत्म हो जाता है तो बैकग्राउंड प्रोसेस भी मर जाता है और यह लटका हुआ नहीं रहता है। जब आपका लॉन्ग कमांड या कमांड खत्म हो जाता है तो किल स्टेटमेंट बैकग्राउंड प्रोसेस को समाप्त कर देता है।
15

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"

नीट और सरल कार्य। आपका बहुत बहुत धन्यवाद!
एंड्रियास क्राफ्ट

यही मैं खोज रहा हूं! बहुत बहुत धन्यवाद :)
wajdi_jurry

4

अधिकांश यूनिक्स कमांड आपको प्रत्यक्ष प्रतिक्रिया का प्रकार नहीं देंगे, जिससे आप ऐसा कर सकते हैं। कुछ आपको stdout या stderr पर आउटपुट देंगे जो आप उपयोग कर सकते हैं।

टार जैसी किसी चीज़ के लिए आप -v स्विच का उपयोग कर सकते हैं और आउटपुट को एक प्रोग्राम में पाइप कर सकते हैं जो प्रत्येक पंक्ति के लिए एक छोटे एनीमेशन को अपडेट करता है जिसे वह पढ़ता है। के रूप में टार फाइलों की एक सूची लिखता है यह unraveled है कार्यक्रम एनीमेशन को अपडेट कर सकता है। एक प्रतिशत पूरा करने के लिए आपको फाइलों की संख्या जानना होगा और लाइनों को गिनना होगा।

जहाँ तक मुझे पता है cp इस तरह का आउटपुट नहीं देता है। Cp की प्रगति की निगरानी के लिए आपको स्रोत और गंतव्य फ़ाइलों की निगरानी करनी होगी और गंतव्य का आकार देखना होगा। आप फ़ाइल आकार प्राप्त करने के लिए स्टेट (2) सिस्टम कॉल का उपयोग करके एक छोटा सी प्रोग्राम लिख सकते हैं । यह स्रोत के आकार को पढ़ेगा और फिर गंतव्य फ़ाइल को प्रदूषित करेगा और तिथि को लिखी गई फ़ाइल के आकार के आधार पर% पूर्ण बार अपडेट करेगा।


4

मेरा समाधान टारबॉल का प्रतिशत प्रदर्शित करता है जो वर्तमान में असम्पीडित और लिखा जा रहा है। 2GB रूट फाइलसिस्टम छवियों को लिखते समय मैं इसका उपयोग करता हूं। आपको वास्तव में इन चीजों के लिए प्रगति पट्टी की आवश्यकता है। मैं जो करता हूं वह उपयोग है gzip --listटारबॉल के कुल असम्पीडित आकार को प्राप्त करने के लिए । उस से मैं फ़ाइल को 100 भागों में विभाजित करने के लिए आवश्यक अवरोधक-कारक की गणना करता हूं। अंत में, मैं प्रत्येक ब्लॉक के लिए एक चेकपॉइंट संदेश प्रिंट करता हूं। 2GB फ़ाइल के लिए यह लगभग 10MB का ब्लॉक देता है। यदि वह बहुत बड़ा है तो आप BLOCKING_FACTOR को 10 या 100 से विभाजित कर सकते हैं, लेकिन फिर प्रतिशत के संदर्भ में सुंदर आउटपुट प्रिंट करना कठिन है।

मान लें कि आप बैश का उपयोग कर रहे हैं तो आप निम्नलिखित शेल फ़ंक्शन का उपयोग कर सकते हैं

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

अच्छा समाधान है लेकिन जब आप एक निर्देशिका को संपीड़ित करना चाहते हैं तो आप कैसे करते हैं?
समीर सेडेक

4

सबसे पहले बार केवल एक पाइप प्रगति मीटर नहीं है। अन्य (शायद और भी अधिक ज्ञात) pv (पाइप दर्शक) है।

दूसरी बार और pv का उपयोग इस तरह किया जा सकता है:

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

या और भी:

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

एक उपयोगी ट्रिक यदि आप कमांड में बार और pv का उपयोग करना चाहते हैं, जो तर्कों में दी गई फाइलों के साथ काम कर रहे हैं, जैसे कि file1 file2 की प्रतिलिपि बनाएँ, प्रक्रिया प्रतिस्थापन का उपयोग करना है :

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

प्रक्रिया प्रतिस्थापन एक मैश मैजिक चीज़ है जो अस्थायी पेंडो पाइप फाइल / dev / fd / बनाता है और इस पाइप के माध्यम से रन की गई प्रक्रिया (कोष्ठक के अंदर) से stdout कनेक्ट करता है और कॉपी इसे एक साधारण फ़ाइल की तरह देखता है (एक अपवाद के साथ, यह केवल इसे पढ़ सकता है। फारवर्ड)।

अपडेट करें:

बार कमांड खुद भी नकल के लिए अनुमति देता है। मैन बार के बाद:

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

लेकिन प्रक्रिया प्रतिस्थापन मेरे विचार में यह करने के लिए अधिक सामान्य तरीका है। यह स्वयं cp प्रोग्राम का उपयोग करता है।


3

मैं - गेज परम के साथ संवाद का उपयोग करना पसंद करता हूं । बहुत बार इनडब पैकेज इंस्टॉलेशन और कई डिस्ट्रो के अन्य बुनियादी कॉन्फ़िगरेशन सामान का उपयोग किया जाता है। तो आपको पहिया को फिर से मजबूत करने की आवश्यकता नहीं है ... फिर से

बस 1 से 100 @stdin तक एक इंट वैल्यू डालें। एक मूल और मूर्खतापूर्ण उदाहरण:

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

मेरे पास यह / बिन / प्रतीक्षा हैखाना पकाने के उद्देश्यों के लिए फ़ाइल (chmod u + x परमिट के साथ): पी

#!/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प्रत्येक फ़ाइलनाम को आउटपुट करेगी क्योंकि यह उसे संपीड़ित करता है) तो एक सरल, तेज, दर्द रहित और अनुकूलन योग्य समाधान मौजूद है।

अजगर मॉड्यूल स्थापित करें 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अजगर कोड में पुनरावृत्तियों को लपेटने के लिए उपयोग कर सकते हैं ।

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


मुझे नहीं लगता कि आपका उदाहरण "अधिक विकल्पों" के साथ काम करता है। यह एक पाइप के माध्यम से tqdmSTDOUT को पारित करने के लिए लगता है wc -l। आप शायद उससे बचना चाहते हैं।
cprn

1
@ इनपुट अपने इनपुट को पाइप करते समय tqdmप्रगति दिखाएगा । इस मामले में वैसा ही इनपुट प्राप्त होता है जैसे कि शामिल नहीं था। STDERRSTDINSTDOUTwc -ltqdm
कैस्पर। डीएल

आह, अब समझ में आता है। समझाने के लिए धन्यवाद।
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

यह केवल सूक्ति क्षेत्र का उपयोग करके लागू होता है। ज़ेनिटी स्क्रिप्ट को कोसने के लिए एक महान देशी इंटरफ़ेस प्रदान करता है: https://help.gnome.org/users/zenity/stable/

ज़ेनिटी प्रोग्रेस बार उदाहरण से:

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

मैंने बार-बार वर्ण स्क्रिप्ट में स्ट्रिंग रिपीटिंग के लिए स्ट्रिंग बनाने से एक उत्तर का उपयोग किया । मेरे पास स्क्रिप्ट्स के लिए दो अपेक्षाकृत छोटे बैश संस्करण हैं, जिन्हें प्रगति बार (उदाहरण के लिए, एक लूप जो कई फ़ाइलों से गुजरता है, लेकिन बड़ी टार फ़ाइलों या कॉपी ऑपरेशन्स के लिए उपयोगी नहीं है) को प्रदर्शित करने की आवश्यकता है। तेज़ में दो फ़ंक्शन होते हैं, एक बार डिस्प्ले के लिए स्ट्रिंग्स तैयार करने के लिए:

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

और एक प्रगति पट्टी प्रदर्शित करने के लिए:

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 "#" वर्णों के साथ बार के लिए तार तैयार करना, और उसके बाद:

progressbar 35 80

35/80 अनुपात से मेल खाने वाले "#" वर्णों की संख्या प्रदर्शित करेगा:

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

ध्यान रखें कि फ़ंक्शन एक ही पंक्ति में बार को तब तक प्रदर्शित करता है जब तक कि आप (या कुछ अन्य कार्यक्रम) एक नई रेखा प्रिंट नहीं करते। यदि आप -1 को पहले पैरामीटर के रूप में रखते हैं, तो बार को मिटा दिया जाएगा:

progressbar -1 80

धीमी संस्करण सभी एक समारोह में है:

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प्रत्येक प्रिंटफ कमांड के अंत में जोड़ें ।


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;

प्रगति के मूल्य / सीमा को जाँचने और प्रदर्शित करने के लिए लूप के अंदर झंडे / चर का उपयोग कर सकते हैं ।


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))"
18-05

0

मैंने इसका लाभ उठाते हुए एक एम्बेडेड सिस्टम के लिए एक शुद्ध शेल संस्करण किया:

  • / usr / bin / dd's SIGUSR1 सिग्नल हैंडलिंग सुविधा।

    असल में, यदि आप 'मार SIGUSR1 $ (pid_of_running_dd_process)' भेजते हैं, तो यह थ्रूपुट गति और हस्तांतरित राशि का एक सारांश आउटपुट करेगा।

  • dd को बैकग्राउंड करना और फिर अपडेट के लिए इसे नियमित रूप से क्वेरी करना, और पुराने स्कूल ftp क्लाइंट की तरह हैश टिक्स बनाना।

  • Scp जैसे गैर-stdout मैत्रीपूर्ण कार्यक्रमों के लिए गंतव्य के रूप में / dev / stdout का उपयोग करना

अंतिम परिणाम आपको किसी भी फाइल ट्रांसफर ऑपरेशन को करने और प्रगति अपडेट प्राप्त करने की अनुमति देता है जो पुराने-स्कूल एफ़टीपी 'हैश' आउटपुट की तरह दिखता है जहां आपको हर एक्स बाइट्स के लिए एक हैश मार्क मिलेगा।

यह शायद ही उत्पादन गुणवत्ता कोड है, लेकिन आपको यह विचार मिलता है। मुझे लगता है कि यह प्यारा है।

इसके लायक क्या है, वास्तविक बाइट-गिनती हैश की संख्या में सही ढंग से परिलक्षित नहीं हो सकती है - आपके पास गोल मुद्दों के आधार पर कम या ज्यादा हो सकता है। परीक्षण स्क्रिप्ट के भाग के रूप में इसका उपयोग न करें, यह सिर्फ आंख कैंडी है। और, हाँ, मुझे पता है कि यह बहुत अक्षम है - यह एक शेल स्क्रिप्ट है और मैं इसके लिए कोई माफी नहीं देता।

अंत में प्रदान किए गए wget, scp और tftp के उदाहरण। यह डेटा उत्सर्जित करने वाली किसी भी चीज़ के साथ काम करना चाहिए। उन कार्यक्रमों के लिए / dev / stdout का उपयोग करना सुनिश्चित करें जो 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डरावना है।

'# मेरा पीआईडी ​​हैंडलिंग गूंगा है' के साथ कॉल करने का प्रयास किया गया
सिंथेसाइपरसेल

आप शायद कैप्चर कर सकते हैं $!से ddऔर पर इंतजार [[ -e /proc/${DD_PID} ]]

0

यदि आपको एक अस्थायी प्रगति बार (अग्रिम समय में पता करके) दिखाना है, तो आप पायथन का उपयोग इस प्रकार कर सकते हैं:

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

फिर, यह मानते हुए कि आपने पायथन लिपि को सहेज लिया है progressbar.py, निम्न कमांड चलाकर अपनी बैश लिपि से प्रगति पट्टी दिखाना संभव है:

python progressbar.py 10 50

यह सेकंड के 50लिए एक प्रगति बार आकार के अक्षर और "रनिंग" दिखाएगा 10


0

मैंने डर से उपलब्ध कराए गए जवाब पर बनाया है

यह एक RMAN पुनर्स्थापना की प्रगति को पुनः प्राप्त करने के लिए एक Oracle डेटाबेस से जुड़ता है।

#!/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 के साथ मानक आउट (लेकिन आपको अनुमानित आउटपुट फ़ाइल आकार के लिए "टोट" सेट करने के लिए स्रोत को संशोधित करना होगा)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.