จะเพิ่มแถบความคืบหน้าลงในเชลล์สคริปต์ได้อย่างไร?


413

เมื่อสคริปต์ใน bash หรือเชลล์อื่น ๆ ใน * NIX ในขณะที่รันคำสั่งที่จะใช้เวลานานกว่าสองสามวินาทีแถบความคืบหน้าเป็นสิ่งจำเป็น

ตัวอย่างเช่นการคัดลอกไฟล์ขนาดใหญ่เปิดไฟล์ tar ขนาดใหญ่

คุณแนะนำวิธีการเพิ่มแถบความคืบหน้าให้กับเชลล์สคริปต์?


ดูเพิ่มเติมที่stackoverflow.com/questions/12498304/…สำหรับตัวอย่างของตรรกะการควบคุม (แบ็คกราวน์งานและทำบางสิ่งจนกว่ามันจะเสร็จสิ้น)
tripleee

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'

ในความคิดเห็นด้านล่าง puk กล่าวถึง "ล้มเหลว" ถ้าคุณเริ่มต้นด้วยบรรทัดยาวแล้วต้องการเขียนบรรทัดสั้น: ในกรณีนี้คุณจะต้องเขียนทับความยาวของบรรทัดยาว (เช่นมีช่องว่าง)


23
ตามหน้าecho man (อย่างน้อยใน MacOS X) sh / bash ใช้คำสั่งechoในตัวที่ไม่ยอมรับ "-n" ... ดังนั้นเพื่อให้บรรลุในสิ่งเดียวกันคุณต้องใส่\ r \ cที่ส่วนท้ายของสตริงแทนที่จะเป็น\ r
Justin Jenkins

51
วิธีแบบพกพาเพื่อการส่งออกนี้คือการใช้แทนprintf echo
Jens

9
สำหรับ printf ที่เราจะต้องใช้รูปแบบนี้: printf "#### (50%%)\r"มันจะไม่ทำงานกับเครื่องหมายคำพูดเดี่ยวและเครื่องหมายเปอร์เซ็นต์จะต้องได้รับการยกเว้น
nurettin

7
ฉันไม่ได้รับสิ่งนี้ - ได้รับการยอมรับและอัปโหลดจำนวนมากสำหรับแฮ็ค "ฉันจะเดาว่าการดำเนินการนี้จะใช้เวลานานเท่าใดกับฮาร์ดแวร์ที่ไม่รู้จัก" pvเป็นคำตอบที่ถูกต้อง IMO (แต่บาร์ก็จะทำเช่นเดียวกัน)
Stephen

19
คำถามคือ "ฉันจะทำแถบความคืบหน้า" กับตัวอย่างของการคัดลอกไฟล์ได้อย่างไร ฉันมุ่งเน้นไปที่ปัญหา "กราฟิก" ไม่ใช่การคำนวณว่าการคัดลอกไฟล์ไปได้ไกลแค่ไหน
Mitch Haile

73

คุณอาจสนใจวิธีการปั่นด้ายด้วย :

ฉันสามารถปั่นด้ายใน Bash ได้หรือไม่?

แน่นอน!

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

แต่ละครั้งที่วนซ้ำวนซ้ำมันจะแสดงตัวอักษรถัดไปในสตริง sp ล้อมรอบเมื่อถึงจุดสิ้นสุด (i คือตำแหน่งของอักขระปัจจุบันที่จะแสดงและ $ {# sp} คือความยาวของสตริง sp)

สตริง \ b ถูกแทนที่ด้วยอักขระ 'backspace' หรือคุณสามารถเล่นกับ \ r เพื่อกลับไปที่จุดเริ่มต้นของบรรทัด

หากคุณต้องการให้มันช้าลงให้ใส่คำสั่ง sleep ภายในลูป (หลังจาก 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อาจต้องใช้ ints มากกว่าทศนิยม)
อดัมแคทซ์

1
@Daenyth ขอบคุณ กรุณาที่เราควรจะเรียกคำสั่งที่เราต้องดูมันคือความคืบหน้าโดยใช้รหัสก่อนหน้า
goro

@goro: ในsome_work ...บรรทัดด้านบน; การอภิปรายรายละเอียดเพิ่มเติมที่สร้างขึ้นบนคำตอบนี้เป็นประโยชน์และแสดงความคิดเห็นที่เป็นประโยชน์อดัมแคทซ์ - ให้ความสำคัญกับการปฏิบัติตาม POSIX - การสามารถพบได้ที่นี่
mklement0

@ 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

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
Adam Katz

48

บางโพสต์ได้แสดงวิธีแสดงความคืบหน้าของคำสั่ง ในการคำนวณคุณต้องดูว่าคุณก้าวหน้าไปมากแค่ไหน ในระบบ BSD คำสั่งบางอย่างเช่น dd (1), รับSIGINFOสัญญาณและจะรายงานความคืบหน้า 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/b0ab6a6aa6e6aa8e1eeaf236b
unhammer

โพสต์ที่ยอดเยี่ยมรักมันเสมอเมื่อawkเล่น!
ShellFish

มันเยี่ยมมาก! ขอบคุณสำหรับสคริปต์ที่ยอดเยี่ยม!
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 ได้มั้ย คุณกำลังทำการทดแทน sed ด้วยตัวแปร _fill และ _empty หรือไม่? ฉันสับสน
Chirag

แทนที่จะใช้ sed ฉันใช้ bash ภายใน "Substring Replacement" เนื่องจากนี่เป็นงานง่ายฉันชอบใช้ฟังก์ชั่นภายในของ bash สำหรับงานประเภทนี้ รหัสดูดีขึ้นเช่นกัน :-) ตรวจสอบที่นี่tldp.org/LDP/abs/html/string-manipulation.htmlและค้นหาการแทนที่ซับสตริง
fearside

และ $ {_ fill} ถูกกำหนดเป็นจำนวนช่องว่าง $ {_ Done} นี่คือสิ่งที่สวยงาม คนงานที่ดี แน่นอนฉันจะใช้สิ่งนี้ในสคริปต์ทุบตีของฉันทุกฮ่าฮ่า
Chirag

ยอดเยี่ยม @fearside! ฉันปรับแต่งเล็กน้อยเพื่อข้ามเมื่อ _progress ไม่เปลี่ยนแปลงจากค่าสุดท้ายเพื่อปรับปรุงความเร็ว github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

หวาน. การเปลี่ยนเส้นประด้วยรูปสี่เหลี่ยมผืนผ้าให้ความรู้สึกและความเป็นมืออาชีพมากขึ้น:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI

44

ใช้คำสั่ง linux pv:

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

ไม่ทราบขนาดถ้าอยู่กลางสตรีม แต่ให้ความเร็วและผลรวมและจากตรงนั้นคุณสามารถทราบได้ว่าควรใช้เวลานานแค่ไหนและรับความคิดเห็นเพื่อให้คุณรู้ว่ามันไม่ได้แขวน


32

ฉันกำลังมองหาบางสิ่งที่เซ็กซี่มากกว่าคำตอบที่เลือกไว้ดังนั้นสคริปต์ของฉันเองก็เช่นกัน

ดูตัวอย่าง

คืบหน้าในการดำเนินการ

แหล่ง

ฉันใส่มันลงบนGitHubprogress-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 ได้อย่างไร? ฉันหมายความว่าเป็นไปได้อย่างไรที่จะวัดระยะเวลาของการอัปเกรด (ตัวอย่าง) บนเซิร์ฟเวอร์ระยะไกล
faceless

1
@ ไม่มีที่ติมันไม่ได้อยู่ในขอบเขตของรหัสนี้ที่คุณให้เวลาและนับถอยหลัง
Édouard Lopez

1
@ ฟิวชั่นเป็นตัวละครยูนิโค้ด (U + 2587 LOWER SEVEN EIGHTHS BLOCK) ที่ควรจะปลอดภัยสำหรับกระสุนสมัยใหม่ ลองใช้คำแนะนำของคุณ
Édouard Lopez

18

tar GNUมีตัวเลือกที่มีประโยชน์ซึ่งให้การทำงานของแถบความคืบหน้าอย่างง่าย

(... ) การกระทำอื่น ๆ ของจุดตรวจสอบที่มีคือ 'dot' (หรือ '.') มันสั่งให้ 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" - carriage return, ถ่านพิเศษเพื่อกลับไปที่จุดเริ่มต้นของบรรทัด

คุณสามารถทำให้มันแสดงผลเนื้อหาได้ทุกความเร็วดังนั้นวิธีนี้จึงเป็นสากลมากเช่นมักจะใช้สำหรับการแสดงภาพ "แฮ็ค" ในภาพยนตร์ที่โง่เง่าไม่ล้อเล่น

คำตอบแบบเต็ม

เนื้อของปัญหาคือวิธีการกำหนด$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 ' ' - แปลหลายช่องว่างเป็นหนึ่ง (บีบพวกเขา)
  • 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 - builtin สำหรับการพิมพ์สิ่งต่าง ๆ ในรูปแบบที่กำหนด
  • 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 เห็น

ฉันดูที่ซอร์สโค้ด C สำหรับ APT และตัดสินใจที่จะเขียนสิ่งที่เทียบเท่าของฉันสำหรับการทุบตี

แถบความคืบหน้านี้จะยังคงอยู่ที่ด้านล่างของเครื่องอย่างดีและจะไม่รบกวนการส่งออกใด ๆ ที่ส่งไปยังเครื่อง

โปรดทราบว่าขณะนี้แถบได้รับการแก้ไขที่ความกว้าง 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

สิ่งนี้จะสร้างอนันต์ในขณะที่วนรอบที่ดำเนินการในพื้นหลังและสะท้อน "" ทุกวินาที. สิ่งนี้จะแสดง.ในเปลือก เรียกใช้tarคำสั่งหรือคำสั่งใด ๆ ที่คุณต้องการ เมื่อเสร็จสิ้นการดำเนินการคำสั่งแล้วฆ่างานที่ผ่านมาทำงานในพื้นหลัง - ซึ่งเป็นที่ที่ไม่มีที่สิ้นสุดในขณะที่ห่วง


ไม่สามารถเริ่มงานใหม่ในพื้นหลังในระหว่างการดำเนินการและอาจถูกฆ่าแทนที่จะวนซ้ำความคืบหน้า?
Centimane

ฉันคิดว่าความคิดนี้คือคุณจะใส่มันลงในสคริปต์ดังนั้นนี่จะเป็นการดักเฉพาะการออกจากสคริปต์นั้น
Iguananaut

1
ฉันรักคำสั่งนี้ฉันใช้มันในไฟล์ของฉัน ฉันรู้สึกไม่สบายใจเล็กน้อยเนื่องจากฉันไม่เข้าใจวิธีการทำงาน บรรทัดแรกและบรรทัดที่สามเข้าใจง่ายกว่า แต่ฉันก็ยังไม่แน่ใจ ฉันรู้ว่านี่เป็นคำตอบที่เก่า แต่มีวิธีที่ฉันจะได้คำอธิบายที่ต่างออกไปสำหรับมือใหม่ในการเขียนโปรแกรม
เฟลิเป้

1
นี่คือคำตอบที่แท้จริงเท่านั้นที่ผู้อื่นเป็นเพียงแค่สคริปต์แถบความคืบหน้าของเล่น 101 สคริปต์ซึ่งไม่ได้มีความหมายอะไรเลยและไม่มีประโยชน์สำหรับโปรแกรมจริงแบบใช้ครั้งเดียวไม่สามารถติดตามได้ (เกือบทั้งหมด) ขอบคุณ.
bekce

@Felipe, the while loop เป็นกระบวนการส่วนหลัง The $! ในกับดักแรกจับ id กระบวนการของกระบวนการพื้นหลังนั้นและตรวจสอบให้แน่ใจว่าถ้ากระบวนการปัจจุบัน / ผู้ปกครองจบกระบวนการพื้นหลังก็จะตายเช่นกันและมันจะไม่ถูกแขวนไว้ คำสั่ง kill จะสิ้นสุดกระบวนการส่วนหลังเมื่อคำสั่งยาวหรือคำสั่งยาวสิ้นสุด
floydn

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

นี่คือสิ่งที่ฉันกำลังค้นหา! ขอบคุณมาก :)
wajdi_jurry

4

คำสั่ง unix ส่วนใหญ่จะไม่ให้ผลตอบรับโดยตรงกับคุณซึ่งคุณสามารถทำได้ บางตัวจะให้เอาต์พุตกับ stdout หรือ stderr ที่คุณสามารถใช้ได้

สำหรับสิ่งที่คล้ายกับ tar คุณสามารถใช้สวิตช์ -v และไพพ์เอาต์พุตไปยังโปรแกรมที่อัพเดตภาพเคลื่อนไหวขนาดเล็กสำหรับแต่ละบรรทัดที่อ่าน ในขณะที่ tar เขียนรายการของไฟล์โปรแกรมจะไม่สามารถอัปเดตภาพเคลื่อนไหวได้ ในการทำเปอร์เซ็นต์ให้สมบูรณ์คุณจะต้องทราบจำนวนไฟล์และนับจำนวนบรรทัด

cp ไม่ได้ให้ผลลัพธ์แบบนี้เท่าที่ฉันรู้ ในการตรวจสอบความคืบหน้าของ cp คุณจะต้องตรวจสอบไฟล์ต้นทางและไฟล์ปลายทางและดูขนาดปลายทาง คุณสามารถเขียนโปรแกรม c ขนาดเล็กโดยใช้การเรียกระบบstat (2)เพื่อรับขนาดไฟล์ สิ่งนี้จะอ่านขนาดของแหล่งที่มาจากนั้นสำรวจไฟล์ปลายทางและอัปเดตแถบ% สมบูรณ์ตามขนาดของไฟล์ที่เขียนจนถึงปัจจุบัน


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

วิธีแก้ปัญหาที่ดี แต่คุณจะทำอย่างไรเมื่อคุณต้องการบีบอัดไดเรกทอรี?
กลั้ว Sadek

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

เคล็ดลับที่มีประโยชน์อย่างหนึ่งหากคุณต้องการใช้ประโยชน์จาก bar และ pv ในคำสั่งที่ทำงานกับไฟล์ที่กำหนดในอาร์กิวเมนต์เช่นคัดลอก file1 file2 คือการใช้การทดแทนกระบวนการ :

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

การทดแทนโพรเซสเป็นสิ่ง bash magic ที่สร้างชั่วคราวไฟล์ fifo pipe / dev / fd / และเชื่อมต่อ stdout จาก runned โพรเซส (ในวงเล็บ) ผ่านไพพ์นี้และ copy เห็นว่ามันเหมือนกับไฟล์ธรรมดา (ยกเว้นเพียงข้อเดียวเท่านั้นที่สามารถอ่านได้ ไปข้างหน้า)

ปรับปรุง:

คำสั่ง bar นั้นอนุญาตให้ทำการคัดลอกได้เช่นกัน After man bar:

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

แต่การทดแทนกระบวนการในความคิดของฉันเป็นวิธีทั่วไปที่จะทำ มันใช้โปรแกรม cp เอง


3

ฉันชอบที่จะใช้กล่องโต้ตอบกับ--gauge param มีการใช้บ่อยมากในการติดตั้งแพ็คเกจ. deb และการกำหนดค่าพื้นฐานอื่น ๆ ของ distros จำนวนมาก ดังนั้นคุณไม่จำเป็นต้องบูรณาการล้อ ... อีกครั้ง

เพียงแค่ใส่ค่า int จาก 1 ถึง 100 @stdin ตัวอย่างพื้นฐานและไร้สาระหนึ่งตัวอย่าง:

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จะส่งชื่อไฟล์แต่ละไฟล์ตามที่บีบอัด) ดังนั้นจึงมีวิธีที่ง่ายกว่ารวดเร็วไม่เจ็บปวดและปรับแต่งได้

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
@cprn tqdmจะแสดงความคืบหน้าในSTDERRขณะที่ท่อปัจจัยการผลิตเพื่อSTDIN STDOUTในกรณีนี้wc -lจะได้รับการป้อนข้อมูลแบบเดียวกับที่tqdmไม่รวม
casper.dcl

อ่าเข้าใจแล้ว ขอบคุณที่อธิบาย
cprn

2

จากการทำงานของ Edouard Lopez ฉันได้สร้างแถบความคืบหน้าที่เหมาะกับขนาดของหน้าจอไม่ว่าจะเป็นอะไรก็ตาม ลองดูสิ

ป้อนคำอธิบายรูปภาพที่นี่

นอกจากนี้ยังโพสต์ใน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 นำเสนออินเทอร์เฟซดั้งเดิมสำหรับสคริปต์ทุบตี: 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

ฉันใช้คำตอบจากการสร้างสตริงของอักขระซ้ำในเชลล์สคริปต์สำหรับการทำซ้ำอักขระ ฉันมีสองรุ่นทุบตีที่ค่อนข้างเล็กสำหรับสคริปต์ที่ต้องแสดงแถบความคืบหน้า (ตัวอย่างเช่นการวนซ้ำที่ต้องผ่านหลายไฟล์ แต่ไม่มีประโยชน์สำหรับไฟล์ tar ขนาดใหญ่หรือการคัดลอก) หนึ่งที่เร็วขึ้นประกอบด้วยสองฟังก์ชั่นหนึ่งที่จะเตรียมสายสำหรับการแสดงบาร์:

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

หากคุณต้องการ ProgressBar บน 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;

หนึ่งสามารถใช้ธง / ตัวแปรภายในห่วงขณะที่การตรวจสอบและแสดงค่า / ขอบเขตของความคืบหน้า


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))"
sergio

0

ฉันใช้รุ่น shell บริสุทธิ์สำหรับระบบฝังตัวโดยใช้ประโยชน์จาก:

  • คุณลักษณะการจัดการสัญญาณ SIGUSR1 / usr / bin / dd

    โดยทั่วไปถ้าคุณส่ง 'kill SIGUSR1 $ (pid_of_running_dd_process)' มันจะแสดงผลสรุปของความเร็วในการรับส่งและจำนวนเงินที่โอน

  • backgrounding dd จากนั้นทำการสืบค้นเป็นประจำเพื่อรับการปรับปรุงและการสร้างเห็บแฮชเช่นไคลเอนต์ ftp โรงเรียนเก่าที่เคยใช้

  • การใช้ / dev / stdout เป็นปลายทางสำหรับโปรแกรมที่เป็นมิตรที่ไม่ใช่ stdout เช่น scp

ผลลัพธ์สุดท้ายช่วยให้คุณสามารถดำเนินการถ่ายโอนไฟล์และรับการอัปเดตความคืบหน้าซึ่งดูเหมือนว่าเอาต์พุต 'hash' ของ FTP แบบเก่าซึ่งคุณจะได้รับเครื่องหมายแฮชสำหรับ X ไบต์ทุกตัว

นี่คือรหัสคุณภาพการผลิตแทบจะไม่ แต่คุณเข้าใจ ฉันคิดว่ามันน่ารัก

สำหรับสิ่งที่คุ้มค่าจำนวนไบต์ที่แท้จริงอาจไม่สะท้อนอย่างถูกต้องในจำนวนแฮช - คุณอาจมีหนึ่งหรือมากกว่านั้นขึ้นอยู่กับปัญหาการปัดเศษ อย่าใช้สิ่งนี้เป็นส่วนหนึ่งของบททดสอบมันเป็นแค่เรื่องน่ารู้ และใช่ฉันรู้ว่านี่ไม่มีประสิทธิภาพมาก - เป็นเชลล์สคริปต์และฉันไม่ขอโทษ

ตัวอย่างด้วย 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นั้นน่ากลัว

พยายามโทรออกด้วย '# การจัดการ PID ของฉันเป็นใบ้'
synthesizerpatel

คุณอาจจะสามารถจับภาพ$!จากและรอคอย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ไปได้ที่จะแสดงแถบความคืบหน้าจากสคริปต์ทุบตีของคุณโดยเรียกใช้คำสั่งต่อไปนี้:

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.