Làm cách nào để thêm thanh tiến trình vào tập lệnh shell?


413

Khi kịch bản trong bash hoặc bất kỳ shell nào khác trong * NIX, trong khi chạy lệnh sẽ mất hơn vài giây, cần có một thanh tiến trình.

Ví dụ: sao chép một tệp lớn, mở tệp tar lớn.

Những cách nào bạn khuyên bạn nên thêm thanh tiến trình vào shell script?


Xem thêm stackoverflow.com/questions/12498304/ Khăn để biết ví dụ về logic điều khiển (làm nền cho công việc và làm một cái gì đó cho đến khi hoàn thành).
tripleee

1
Có một bộ các yêu cầu chúng ta thường thấy hữu ích khi viết kịch bản. ghi nhật ký, hiển thị tiến trình, màu sắc, đầu ra ưa thích, v.v ... Tôi luôn cảm thấy nên có một loại khung kịch bản đơn giản. Cuối cùng tôi đã quyết định thực hiện một vì tôi không thể tìm thấy bất kỳ. Bạn có thể thấy điều này hữu ích. Đó là trong bash thuần túy, ý tôi là Just Bash. github.com/SumuduLansakara/JustBash
Sumudu

Không nên chuyển nó sang unix.stackexchange.com ?
Ethan

Câu trả lời:


685

Bạn có thể thực hiện điều này bằng cách ghi đè một dòng. Sử dụng \rđể quay lại đầu dòng mà không ghi \nvào thiết bị đầu cuối.

Viết \nkhi bạn hoàn thành để tiến lên dòng.

Sử dụng echo -neđể:

  1. không in \n
  2. để nhận ra các chuỗi thoát như thế nào \r.

Đây là bản demo:

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

Trong một bình luận bên dưới, puk đề cập đến "thất bại" này nếu bạn bắt đầu bằng một dòng dài và sau đó muốn viết một dòng ngắn: Trong trường hợp này, bạn sẽ cần ghi đè lên độ dài của dòng dài (ví dụ: có khoảng trắng).


23
Theo trang echo man (ít nhất là trên MacOS X), sh / bash sử dụng lệnh echo tích hợp riêng của họ không chấp nhận "-n" ... vì vậy để thực hiện điều tương tự bạn cần đặt \ r \ c ở cuối chuỗi, thay vì chỉ \ r
Justin Jenkins

51
Cách di động để đầu ra này là sử dụng printfthay vì echo.
Jens

9
đối với printf, chúng ta sẽ phải sử dụng định dạng này : printf "#### (50%%)\r", nó sẽ không hoạt động với dấu ngoặc đơn và dấu phần trăm cần được thoát.
Nurettin

7
Tôi không nhận được điều này - được chấp nhận và hàng đống upvote cho một bản hack "Tôi sẽ đoán hoạt động này sẽ mất bao lâu cho phần cứng không xác định"? pv là câu trả lời đúng IMO (nhưng thanh cũng sẽ làm như vậy)
Stephen

19
Câu hỏi là "Làm thế nào để tôi thực hiện các thanh tiến trình" với một ví dụ về sao chép tệp. Tôi tập trung vào vấn đề "đồ họa", chứ không phải tính toán thời gian thực hiện thao tác sao chép tệp.
Mitch Haile

73

Bạn cũng có thể quan tâm đến cách làm một spinner :

Tôi có thể làm một spinner trong Bash?

Chắc chắn rồi!

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

Mỗi khi vòng lặp lặp lại, nó sẽ hiển thị ký tự tiếp theo trong chuỗi sp, bao quanh khi nó kết thúc. (i là vị trí của ký tự hiện tại sẽ hiển thị và $ {# sp} là độ dài của chuỗi sp).

Chuỗi \ b được thay thế bằng ký tự 'backspace'. Ngoài ra, bạn có thể chơi với \ r để quay lại đầu dòng.

Nếu bạn muốn nó chạy chậm lại, hãy đặt lệnh ngủ bên trong vòng lặp (sau printf).

Tương đương POSIX sẽ là:

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

Nếu bạn đã có một vòng lặp thực hiện rất nhiều công việc, bạn có thể gọi hàm sau vào đầu mỗi lần lặp để cập nhật spinner:

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
Phiên bản ngắn hơn nhiều, hoàn toàn di động *: while :;do for s in / - \\ \|; do printf "\r$s";sleep .1;done;done(*: sleepcó thể yêu cầu số nguyên thay vì số thập phân)
Adam Katz

1
@Daenyth. Cảm ơn. Vui lòng gọi lệnh mà chúng ta cần xem đó là tiến trình sử dụng mã trước đó?
goro

@goro: Trong some_work ...dòng trên; một cuộc thảo luận chi tiết hơn dựa trên câu trả lời hữu ích này và nhận xét hữu ích của Adam Katz - với trọng tâm là tuân thủ POSIX - có thể được tìm thấy ở đây .
mkuity0

@AdamKatz: Đó là một đơn giản hóa hữu ích, di động, nhưng để phù hợp với cách tiếp cận của Daenyth, công cụ quay vòng phải dựa trên \bchứ không phải \r, vì nó sẽ chỉ hoạt động ở đầu dòng: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- hoặc, nếu hiển thị con trỏ phía sau công cụ quay vòng là không mong muốn:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mkuity0

1
@kaushal - Ctrl + C sẽ dừng thủ công. Nếu bạn có một công việc nền, bạn có thể lưu trữ PID ( job=$!) và sau đó chạy while kill -0 $job 2>/dev/null;do …, ví dụ: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

Một số bài viết đã chỉ ra cách hiển thị tiến trình của lệnh. Để tính toán, bạn sẽ cần xem bạn đã tiến bộ bao nhiêu. Trên các hệ thống BSD, một số lệnh, chẳng hạn như dd (1), chấp nhận SIGINFOtín hiệu và sẽ báo cáo tiến trình của chúng. Trên các hệ thống Linux, một số lệnh sẽ đáp ứng tương tự SIGUSR1. Nếu cơ sở này có sẵn, bạn có thể dẫn đầu vào của bạn thông quadd để theo dõi số lượng byte được xử lý.

Ngoài ra, bạn có thể sử dụng lsofđể lấy phần bù của con trỏ đọc của tệp và từ đó tính toán tiến trình. Tôi đã viết một lệnh, được đặt tên là pmonitor , hiển thị tiến trình xử lý một quy trình hoặc tệp được chỉ định. Với nó, bạn có thể làm những việc, chẳng hạn như sau.

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

Một phiên bản trước đó của tập lệnh shell Linux và FreeBSD xuất hiện trên blog của tôi .


Điều này thật tuyệt vời, tôi luôn quên chuyển mọi thứ qua pv :-) Tôi nghĩ rằng lệnh "stat" của tôi hoạt động hơi khác một chút, phiên bản (Linux) của tập lệnh này: gist.github.com/unhammer/b0ab6a6aa8e1eeaf236b
unhammer

Bài đăng tuyệt vời, luôn luôn thích nó khi awkchơi!
ShellFish

Điều đó thật tuyệt! Cảm ơn kịch bản tuyệt vời!
thebeagle

47

Có một chức năng thanh tiến trình dễ dàng mà tôi đã viết vào ngày khác:

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

Hoặc lấy từ,
https://github.com/fearside/ProTHERBar/


bạn có thể giải thích dòng dưới 1.2.1.1 không? Bạn có đang thực hiện thay thế sed với các biến _fill và _empty không? Tôi bối rối.
Chirag

Thay vì sử dụng sed, tôi sử dụng bash Internal "Sub chuỗi thay thế", vì đây là một công việc dễ dàng, tôi thích sử dụng các chức năng nội bộ của bash cho loại công việc này. Mã trông cũng đẹp hơn. :-) Kiểm tra tại đây tldp.org/LDP/abs/html/opes-manipulation.html và tìm kiếm thay thế chuỗi con.
sợ hãi

và $ {_ fill} được chỉ định là $ {_ xong} số lượng khoảng trắng. Thật là đẹp Người đàn ông công việc tuyệt vời. Tôi chắc chắn sẽ sử dụng điều này trong tất cả các kịch bản bash của mình haha
Chirag

Công việc tuyệt vời @fearside! Tôi đã thực hiện một số điều chỉnh nhỏ để bỏ qua khi _prowards không thay đổi từ giá trị cuối cùng, để cải thiện tốc độ. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

Ngọt. Thay đổi dấu gạch ngang bằng hình chữ nhật mang lại cho nó một giao diện chuyên nghiệp hơn:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Mehdi LAMRANI

44

sử dụng lệnh linux pv:

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

nó không biết kích thước nếu nó ở giữa dòng, nhưng nó cho tốc độ và tổng cộng và từ đó bạn có thể tìm ra nó sẽ mất bao lâu và nhận phản hồi để bạn biết nó không bị treo.


32

Tôi đang tìm kiếm một cái gì đó gợi cảm hơn câu trả lời được chọn, kịch bản của riêng tôi cũng vậy.

Xem trước

tiến độ-bar.sh trong hành động

Nguồn

Tôi đặt nó trên 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
}

Sử dụng

 progress-bar 100

1
Tôi không hiểu làm thế nào điều này được tích hợp vào một số xử lý trong đó độ dài của quá trình không được biết. Làm cách nào để dừng thanh tiến trình nếu quá trình của tôi kết thúc sớm hơn, ví dụ như giải nén tệp.
Tháng Một

Tôi nghĩ nên sử dụngprogress-bar 100
jirarium

Tiến độ hấp dẫn thực sự. Làm thế nào nó có thể được gắn với một chức năng xử lý hành động kéo dài trên các máy chủ từ xa qua ssh? Ý tôi là làm thế nào có thể đo thời gian nâng cấp (ví dụ) trên các máy chủ từ xa?
vô danh

1
@facless không nằm trong phạm vi của mã này, bạn cung cấp thời gian và nó đếm ngược
Édouard Lopez

1
@Fusion đó là một ký tự unicode (U + 2587 LOWER SEVEN EIGHTHS BLOCK) nên an toàn cho lớp vỏ hiện đại. Hãy thử xem envs của bạn
Édouard Lopez

18

GNU tar có một tùy chọn hữu ích cung cấp chức năng của thanh tiến trình đơn giản.

(...) Một hành động điểm kiểm tra khả dụng khác là 'chấm' (hoặc '.'). Nó hướng dẫn tar in một dấu chấm đơn trên luồng danh sách tiêu chuẩn, ví dụ:

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

Hiệu ứng tương tự có thể đạt được bởi:

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

+1 cho cách tiếp cận đơn giản nhất! Nếu bạn thấy không có dấu chấm nào được in, hãy thử giảm số lượng, ví dụ --checkpoint=.10. Nó cũng hoạt động tuyệt vời khi giải nén với tar -xz.
Noam Manos

13

Một phương pháp đơn giản hơn hoạt động trên hệ thống của tôi bằng tiện ích 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

Không thấy bất cứ điều gì tương tự và tất cả các chức năng tùy chỉnh ở đây dường như chỉ tập trung vào kết xuất, vì vậy ... giải pháp tuân thủ POSIX rất đơn giản của tôi dưới đây với các giải thích từng bước vì câu hỏi này không tầm thường.

TL; DR

Kết xuất thanh tiến trình là rất dễ dàng. Ước tính bao nhiêu của nó sẽ kết xuất là một vấn đề khác nhau. Đây là cách kết xuất (hoạt hình) thanh tiến trình - bạn có thể sao chép và dán ví dụ này vào một tệp và chạy nó:

#!/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} - các giá trị từ 1 đến 20
  • echo -n - in không có dòng mới ở cuối
  • echo -e - giải thích các ký tự đặc biệt trong khi in
  • "\r" - Vận chuyển trở lại, một char đặc biệt để trở về đầu dòng

Bạn có thể làm cho nó hiển thị bất kỳ nội dung nào ở bất kỳ tốc độ nào, vì vậy phương pháp này rất phổ biến, ví dụ như thường được sử dụng để trực quan hóa "hack" trong các bộ phim ngớ ngẩn, không đùa.

Câu trả lời đầy đủ

Phần cốt lõi của vấn đề là làm thế nào để xác định $igiá trị, tức là bao nhiêu thanh tiến trình sẽ hiển thị. Trong ví dụ trên tôi chỉ để nó tăng theo forvòng lặp để minh họa nguyên tắc nhưng một ứng dụng thực tế sẽ sử dụng một vòng lặp vô hạn và tính toán $ibiến trên mỗi lần lặp. Để thực hiện tính toán, nó cần các thành phần sau:

  1. có bao nhiêu công việc phải làm
  2. bao nhiêu công việc đã được thực hiện cho đến nay

Trong trường hợp cpnó cần kích thước của tệp nguồn và kích thước của tệp đích:

#!/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 - kiểm tra số liệu tập tin
  • -c - trả về giá trị định dạng
  • %s - tổng kích thước

Trong trường hợp các thao tác như giải nén tệp, việc tính toán kích thước nguồn hơi khó khăn hơn một chút nhưng vẫn dễ như lấy kích thước của tệp không nén:

#!/bin/sh
src_size=$(gzip -l "$src" | tail -n1 | tr -s ' ' | cut -d' ' -f3)
  • gzip -l - hiển thị thông tin về kho lưu trữ zip
  • tail -n1 - làm việc với 1 dòng từ dưới lên
  • tr -s ' ' - dịch nhiều khoảng trắng thành một (ép chúng)
  • cut -d' ' -f3 - cắt cột phân cách không gian thứ 3

Đây là phần cốt lõi của vấn đề. Giải pháp này ngày càng ít chung chung. Do đó, tất cả các tính toán về tiến trình thực tế đều bị ràng buộc chặt chẽ với miền mà bạn đang cố gắng hình dung, đó có phải là một thao tác tệp duy nhất, đếm ngược thời gian, số lượng tệp tăng lên trong một thư mục, hoạt động trên nhiều tệp, v.v. không thể được sử dụng lại. Phần duy nhất có thể tái sử dụng là kết xuất thanh tiến trình. Để sử dụng lại, bạn cần trừu tượng hóa nó và lưu trong một tệp (ví dụ /usr/lib/progress_bar.sh), sau đó xác định các hàm tính giá trị đầu vào cụ thể cho miền của bạn. Đây là cách một mã tổng quát có thể trông như thế nào (Tôi cũng đã tạo ra sự $BARnăng động bởi vì mọi người đang yêu cầu nó, phần còn lại sẽ rõ ràng ngay bây giờ):

#!/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 - một nội dung để in các công cụ theo định dạng nhất định
  • printf '%50s' - không in gì, đệm nó với 50 dấu cách
  • tr ' ' '#' - dịch mọi chỗ trống sang dấu băm

Và đây là cách bạn sử dụng nó:

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

Rõ ràng nó có thể được bọc trong một chức năng, được viết lại để làm việc với các luồng được truyền, viết lại sang ngôn ngữ khác, bất kể là chất độc của bạn.


1
Đối với những người muốn những thứ đơn giản nhất, tôi chỉ làm cho tôi với câu trả lời đầu tiên của cprn. Đó là một thanh tiến trình rất đơn giản trong một hàm sử dụng một số quy tắc tỷ lệ ngu ngốc để vẽ thanh: pastebin.com/9imhRLYX
YCN-


9

Thanh tiến trình kiểu APT (Không phá vỡ đầu ra bình thường)

nhập mô tả hình ảnh ở đây

EDIT: Để biết phiên bản cập nhật, hãy kiểm tra trang github của tôi

Tôi không hài lòng với câu trả lời cho câu hỏi này. Những gì cá nhân tôi đang tìm kiếm là một thanh tiến trình ưa thích như được thấy bởi APT.

Tôi đã xem mã nguồn C cho APT và quyết định viết tương đương của riêng tôi cho bash.

Thanh tiến trình này sẽ ở độc đáo ở dưới cùng của thiết bị đầu cuối và sẽ không can thiệp vào bất kỳ đầu ra nào được gửi đến thiết bị đầu cuối.

Xin lưu ý rằng thanh hiện đang được cố định ở độ rộng 100 ký tự. Nếu bạn muốn mở rộng quy mô của thiết bị đầu cuối, điều này cũng khá dễ thực hiện (Phiên bản cập nhật trên trang github của tôi xử lý tốt việc này).

Tôi sẽ đăng kịch bản của tôi ở đây. Ví dụ sử dụng:

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

Kịch bản (tôi thực sự khuyên dùng phiên bản trên github của tôi):

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

Điều này cho phép bạn hình dung rằng một lệnh vẫn đang thực thi:

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

Điều này sẽ tạo ra một vòng lặp while vô hạn thực thi trong nền và lặp lại dấu "." mỗi giây Điều này sẽ hiển thị .trong vỏ. Chạy tarlệnh hoặc bất kỳ lệnh nào bạn muốn. Khi lệnh đó kết thúc thực thi rồi giết công việc cuối cùng đang chạy trong nền - đó là vòng lặp while vô hạn .


Không thể một công việc khác bắt đầu trong nền trong khi thực hiện và có khả năng bị giết thay vì vòng lặp tiến trình?
Centimane

Tôi nghĩ ý tưởng là bạn sẽ đặt nó trong một kịch bản, vì vậy điều này sẽ chỉ bẫy một lối thoát của kịch bản đó.
Iguananaut

1
Tôi thích lệnh này, tôi đang sử dụng nó trong các tập tin của tôi. Tôi chỉ hơi khó chịu vì tôi không thực sự hiểu nó hoạt động như thế nào. Dòng đầu tiên và thứ ba dễ hiểu hơn, nhưng tôi vẫn không chắc chắn. Tôi biết đây là một câu trả lời cũ, nhưng có cách nào để tôi có thể nhận được một lời giải thích khác hướng đến người mới khi lập trình
Felipe

1
Đây là câu trả lời đúng DUY NHẤT, trong đó những người khác chỉ là các thanh tiến trình đồ chơi Scripting 101 có nghĩa là không có gì và không được sử dụng cho các chương trình thực tế, một lần, không thể theo dõi (gần như TẤT CẢ). Cảm ơn bạn.
bekce

@Felipe, Vòng lặp while là một quá trình nền. $! trong cái bẫy đầu tiên nắm bắt id tiến trình của quá trình nền đó và đảm bảo rằng nếu quy trình hiện tại / cha mẹ kết thúc thì quá trình nền cũng chết và nó không bị treo. Lệnh kill chỉ kết thúc quá trình nền khi lệnh dài hoặc lệnh của bạn kết thúc.
floydn

7

Đây là cách nó có thể nhìn

Đang tải lên một tập tin

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

Chờ đợi một công việc để hoàn thành

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

Chức năng đơn giản thực hiện nó

Bạn chỉ có thể sao chép-dán nó vào kịch bản của bạn. Nó không yêu cầu bất cứ điều gì khác để làm việc.

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

Ví dụ sử dụng

Ở đây, chúng tôi tải lên một tệp và vẽ lại thanh tiến trình ở mỗi lần lặp. Không quan trọng công việc nào thực sự được thực hiện miễn là chúng ta có thể nhận được 2 giá trị: giá trị tối đa và giá trị hiện tại.

Trong ví dụ bên dưới giá trị tối đa là file_sizevà giá trị hiện tại được cung cấp bởi một số hàm và được gọi 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"

Chức năng gọn gàng và đơn giản. Cảm ơn rất nhiều!
Andreas Kraft

Đây là những gì tôi đang tìm kiếm! Cảm ơn rất nhiều :)
wajdi_jurry

4

Hầu hết các lệnh unix sẽ không cung cấp cho bạn loại phản hồi trực tiếp mà bạn có thể thực hiện việc này. Một số sẽ cung cấp cho bạn đầu ra trên thiết bị xuất chuẩn hoặc thiết bị xuất chuẩn mà bạn có thể sử dụng.

Đối với một cái gì đó như tar, bạn có thể sử dụng công tắc -v và chuyển đầu ra sang một chương trình cập nhật một hình ảnh động nhỏ cho mỗi dòng mà nó đọc. Khi tar viết ra một danh sách các tập tin, chương trình có thể cập nhật hoạt hình. Để hoàn thành phần trăm, bạn sẽ phải biết số lượng tệp và đếm số dòng.

cp không cung cấp loại đầu ra như tôi biết. Để theo dõi tiến trình của cp, bạn sẽ phải theo dõi các tệp nguồn và đích và xem kích thước của đích. Bạn có thể viết một chương trình c nhỏ bằng cách sử dụng lệnh gọi hệ thống stat (2) để lấy kích thước tệp. Điều này sẽ đọc kích thước của nguồn sau đó thăm dò tệp đích và cập nhật thanh hoàn thành% dựa trên kích thước của tệp được ghi cho đến ngày.


4

Giải pháp của tôi hiển thị tỷ lệ phần trăm của tarball hiện không được nén và viết. Tôi sử dụng điều này khi viết ra các hình ảnh hệ thống tập tin gốc 2GB. Bạn thực sự cần một thanh tiến trình cho những điều này. Những gì tôi làm là sử dụng gzip --list để có được tổng kích thước không nén của tarball. Từ đó tôi tính toán hệ số chặn cần thiết để chia tệp thành 100 phần. Cuối cùng, tôi in một thông báo điểm kiểm tra cho mỗi khối. Đối với tệp 2GB, điều này mang lại khoảng 10 MB một khối. Nếu nó quá lớn thì bạn có thể chia BLOCKING_FACTOR cho 10 hoặc 100, nhưng sau đó khó in được sản lượng đẹp hơn theo tỷ lệ phần trăm.

Giả sử bạn đang sử dụng Bash thì bạn có thể sử dụng hàm shell sau

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

Giải pháp tốt nhưng làm thế nào khi bạn muốn nén một thư mục?
Samir Sadek

4

Trước hết thanh không phải là đồng hồ đo tiến độ duy nhất. Cái khác (có thể còn được biết đến nhiều hơn) là pv (ống xem).

Thứ hai thanh và pv có thể được sử dụng ví dụ như thế này:

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

hoặc thậm chí:

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

Một mẹo hữu ích nếu bạn muốn sử dụng thanh và pv trong các lệnh đang làm việc với các tệp được đưa ra trong các đối số, ví dụ như sao chép tệp1 tệp2, là sử dụng thay thế quy trình :

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

Thay thế quy trình là một điều kỳ diệu bash tạo ra các tệp ống fifo tạm thời / dev / fd / và kết nối thiết bị xuất chuẩn từ quy trình đã chạy (bên trong dấu ngoặc đơn) thông qua ống này và sao chép xem nó giống như một tệp thông thường (với một ngoại lệ, nó chỉ có thể đọc được chuyển tiếp).

Cập nhật:

lệnh thanh chính nó cũng cho phép sao chép. Sau thanh người đàn ông:

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

Nhưng theo tôi, quá trình thay thế là cách chung hơn để làm điều đó. Một nó sử dụng chương trình cp chính nó.


3

Tôi thích sử dụng hộp thoại với --gauge param. Được sử dụng rất thường xuyên trong cài đặt gói .deb và các công cụ cấu hình cơ bản khác của nhiều bản phát hành. Vì vậy, bạn không cần phải phát minh lại bánh xe ... một lần nữa

Chỉ cần đặt một giá trị int từ 1 đến 100 @stdin. Một ví dụ cơ bản và ngớ ngẩn:

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

Tôi có tệp / bin / Wait này (với chmod u + x perms) cho mục đích nấu ăn: 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"

Vì vậy, tôi có thể đặt:

Wait "34 min" "warm up the oven"

hoặc là

Wait "dec 31" "happy new year"


2

Đối với tôi dễ sử dụng nhất và tìm kiếm tốt nhất cho đến nay là mệnh lệnh pvhoặc bargiống như một số người đã viết

ví dụ: cần tạo bản sao lưu toàn bộ ổ đĩa với dd

thông thường bạn sử dụng dd if="$input_drive_path" of="$output_file_path"

với pvbạn có thể làm cho nó như thế này:

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

và tiến trình trực tiếp STDOUTnhư sau:

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

sau khi nó được thực hiện tóm tắt đi lên

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

Bạn có thể sử dụng pvhoặc barđể trực quan hóa tiến trình của các quá trình khác nhau, ví dụ đếm ngược thời gian, vị trí trong tệp văn bản, cài đặt ứng dụng của bạn, thiết lập thời gian chạy, v.v.?
cprn

2

Nhiều câu trả lời mô tả việc viết các lệnh của riêng bạn để in ra '\r' + $some_sort_of_progress_msg. Vấn đề đôi khi là việc in ra hàng trăm bản cập nhật này mỗi giây sẽ làm chậm quá trình.

Tuy nhiên, nếu bất kỳ quy trình nào của bạn tạo ra đầu ra (ví dụ: 7z a -r newZipFile myFoldersẽ xuất từng tên tệp khi nén nó) thì sẽ tồn tại một giải pháp đơn giản, nhanh chóng, không đau và có thể tùy chỉnh.

Cài đặt mô-đun 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

Trợ giúp : tqdm -h. Một ví dụ sử dụng nhiều tùy chọn hơn:

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

Là một phần thưởng, bạn cũng có thể sử dụng tqdmđể bọc iterables trong mã python.

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


Tôi không nghĩ rằng ví dụ của bạn với "nhiều lựa chọn hơn" hoạt động. Nó dường như vượt qua tqdmSTDOUT để wc -lthông qua một đường ống. Bạn có thể muốn thoát khỏi điều đó.
cprn

1
@cprn tqdmsẽ hiển thị tiến trình STDERRtrong khi chuyển đầu vào của nó STDINsang STDOUT. Trong trường hợp này, bạn wc -lsẽ nhận được đầu vào giống như thể tqdmkhông được bao gồm.
casper.dcl

Ah, có ý nghĩa bây giờ. Cảm ơn vì đã giải thích.
cprn

2

Dựa trên công việc của Edouard Lopez, tôi đã tạo ra một thanh tiến trình phù hợp với kích thước của màn hình, bất kể đó là gì. Kiểm tra nó ra.

nhập mô tả hình ảnh ở đây

Nó cũng được đăng trên 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

Thưởng thức



1

Điều này chỉ được áp dụng bằng cách sử dụng gnome zquil. Zenity cung cấp giao diện gốc tuyệt vời cho các tập lệnh bash: https://help.gnome.org/users/zenity/ sóng /

Từ thanh tiến trình Zenity Ví dụ:

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

Tôi đã sử dụng một câu trả lời từ Tạo chuỗi các ký tự lặp lại trong tập lệnh shell để lặp lại char. Tôi có hai phiên bản bash tương đối nhỏ cho các tập lệnh cần hiển thị thanh tiến trình (ví dụ: một vòng lặp đi qua nhiều tệp, nhưng không hữu ích cho các tệp tar lớn hoặc các hoạt động sao chép). Công cụ nhanh hơn bao gồm hai chức năng, một chức năng để chuẩn bị các chuỗi để hiển thị thanh:

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

và một để hiển thị thanh tiến trình:

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
}

Nó có thể được sử dụng như:

preparebar 50 "#"

có nghĩa là chuẩn bị các chuỗi cho thanh có 50 ký tự "#" và sau đó:

progressbar 35 80

sẽ hiển thị số lượng ký tự "#" tương ứng với tỷ lệ 35/80:

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

Xin lưu ý rằng chức năng hiển thị thanh trên cùng một dòng cho đến khi bạn (hoặc một số chương trình khác) in một dòng mới. Nếu bạn đặt -1 làm tham số đầu tiên, thanh sẽ bị xóa:

progressbar -1 80

Phiên bản chậm hơn là tất cả trong một chức năng:

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
}

và nó có thể được sử dụng như (ví dụ tương tự như trên):

progressbar 35 80 50

Nếu bạn cần thanh tiến trình trên stderr, chỉ cần thêm >&2vào cuối mỗi lệnh printf.


1

Để chỉ ra tiến trình hoạt động, hãy thử các lệnh sau:

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;

HOẶC LÀ

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;

HOẶC LÀ

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;

HOẶC LÀ

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;

Người ta có thể sử dụng cờ / biến trong vòng lặp while để kiểm tra và hiển thị giá trị / mức độ tiến triển.


1

Sử dụng các đề xuất được liệt kê ở trên, tôi quyết định thực hiện thanh tiến trình của riêng tôi.

#!/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
Đẹp! để làm cho nó hoạt động tôi đã phải thay đổi dòng percent_none="$(( 100 - "$percent_done" ))"thànhpercent_none="$(( 100 - $percent_done))"
sergio

0

Tôi đã thực hiện một phiên bản shell thuần cho một hệ thống nhúng tận dụng:

  • / usr / bin / dd tính năng xử lý tín hiệu SIGUSR1.

    Về cơ bản, nếu bạn gửi 'kill SIGUSR1 $ (pid_of_rasty_dd_ process)', nó sẽ đưa ra một bản tóm tắt về tốc độ thông lượng và số tiền được chuyển.

  • nền tảng dd và sau đó truy vấn nó thường xuyên để cập nhật và tạo ra các dấu băm như các máy khách ftp trường cũ được sử dụng.

  • Sử dụng / dev / stdout làm đích cho các chương trình thân thiện không xuất sắc như scp

Kết quả cuối cùng cho phép bạn thực hiện bất kỳ thao tác chuyển tệp nào và nhận cập nhật tiến trình trông giống như đầu ra 'băm' FTP của trường học cũ, nơi bạn chỉ cần lấy dấu băm cho mỗi byte X.

Đây không phải là mã chất lượng sản xuất, nhưng bạn có được ý tưởng. Tôi nghĩ nó dễ thương đấy.

Đối với giá trị của nó, số byte thực tế có thể không được phản ánh chính xác trong số lần băm - bạn có thể có nhiều hơn hoặc ít hơn tùy thuộc vào các vấn đề làm tròn. Đừng sử dụng nó như là một phần của kịch bản thử nghiệm, nó chỉ là trò chơi mắt. Và, vâng, tôi biết điều này là không hiệu quả khủng khiếp - đó là một kịch bản shell và tôi không đưa ra lời xin lỗi nào cho nó.

Ví dụ với wget, scp và tftp được cung cấp ở cuối. Nó nên hoạt động với bất cứ thứ gì phát ra dữ liệu. Đảm bảo sử dụng / dev / stdout cho các chương trình không thân thiện với thiết bị xuất chuẩn.

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

Ví dụ:

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

Ý tưởng hay, miễn là bạn có kích thước tệp trước thời hạn, bạn có thể cung cấp giá trị gia tăng hơn pv theo cách này, nhưng báo hiệu mù quáng pidof ddlà đáng sợ.

Đã cố gắng gọi điều đó với '# Xử lý PID của tôi bị câm'
tổng

Bạn có thể có thể chụp $!từ ddvà chờ đợi [[ -e /proc/${DD_PID} ]].

0

Trong trường hợp bạn phải hiển thị thanh tiến trình tạm thời (bằng cách biết trước thời gian hiển thị), bạn có thể sử dụng Python như sau:

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

Sau đó, giả sử bạn đã lưu tập lệnh Python như progressbar.py, có thể hiển thị thanh tiến trình từ tập lệnh bash của bạn bằng cách chạy lệnh sau:

python progressbar.py 10 50

Nó sẽ hiển thị một 50ký tự có kích thước thanh tiến trình và "chạy" trong 10vài giây.


0

Tôi đã xây dựng dựa trên câu trả lời được cung cấp bởi sợ hãi

Điều này kết nối với cơ sở dữ liệu Oracle để lấy tiến trình khôi phục 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

}

có thể tạo một hàm rút ra điều này theo tỷ lệ 1-10 cho số lượng thanh:

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;

đầu ra:

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

lưu ý: nếu thay vì 255 bạn đặt 1, bạn sẽ theo dõi tiêu chuẩn trong ... với 2 tiêu chuẩn ra (nhưng bạn phải sửa đổi nguồn để đặt "tot" thành kích thước tệp đầu ra dự kiến)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.