Tạo một thanh tiến trình trong bash


13

Làm cách nào để tạo thanh tiến trình với bash?

Đây là kịch bản của tôi:

 #!/bin/bash
 pass='number1 number12 number13 number14 number15 number16'
 chk='number14'
 for i in $pass ; do
 if [ "$i" == "$chk" ]; then
 echo ' Found ^_^'
 else
 echo 'loading 50%'
 fi
 done

Tôi muốn thay thế echo 'loading 50%'bằng bất cứ điều gì để tạo ra một thanh tiến trình.


1
Một thanh tiến trình trong thiết bị đầu cuối hoặc một thanh tiến trình trong một cửa sổ GUI riêng?
Chỉ huy Byte

2
Trong nhà ga
Black Hawk

etathể làm những gì bạn muốn.
aioobe

Câu trả lời:


14

whiptail được cài đặt sẵn trên Ubuntu và nhiều bản phát hành khác, và sẽ hiển thị các yếu tố tiến trình toàn màn hình (nhưng vẫn dựa trên thiết bị đầu cuối).

dialoglà một superset của whiptail, vì vậy ví dụ này sẽ hoạt động tốt như nhau với cả hai. Nó cung cấp các yếu tố UI nâng cao hơn, vì vậy nó có thể hữu ích nếu bạn đang tìm kiếm sự tương tác của người dùng như bộ chọn tệp và biểu mẫu, nhưng nó có nhược điểm là không được cài đặt sẵn trên nhiều hệ thống.

roi vọt

hộp thoại

for i in $(seq 1 100)
do
    sleep 0.1 
    echo $i
done | whiptail --title 'Test script' --gauge 'Running...' 6 60 0

Lưu ý rằng đầu ra tập lệnh được hiểu theo tỷ lệ phần trăm, do đó bạn có thể phải điều chỉnh đầu ra của mình cho phù hợp.

Whiptail và Dialog cũng cho phép bạn sửa đổi văn bản trong thời gian chạy thông qua một cú pháp khá khó hiểu:

phases=( 
    'Locating Jebediah Kerman...'
    'Motivating Kerbals...'
    'Treating Kessler Syndrome...'
    'Recruiting Kerbals...'
)   

for i in $(seq 1 100); do  
    sleep 0.1

    if [ $i -eq 100 ]; then
        echo -e "XXX\n100\nDone!\nXXX"
    elif [ $(($i % 25)) -eq 0 ]; then
        let "phase = $i / 25"
        echo -e "XXX\n$i\n${phases[phase]}\nXXX"
    else
        echo $i
    fi 
done | whiptail --title 'Kerbal Space Program' --gauge "${phases[0]}" 6 60 0

pvcho thấy tiến trình của một tập tin hoặc luồng được truyền qua nó. Tuy nhiên, nó không thể được (dễ dàng?) Được sử dụng để hiển thị tiến trình của một hoạt động tùy chỉnh như vòng lặp. Nó được thiết kế dành riêng cho các luồng.

$ head -c 1G < /dev/urandom | pv -s 1G > /dev/null
 277MB 0:00:16 [17.4MB/s] [========>                           ] 27% ETA 0:00:43

Một số ví dụ trong thế giới thực pvcó ích:

# progress while importing a DB dump
pv mybigfile.sql | mysql -uroot -p dbname

# importing straight from a remote server
ssh user@server 'cat mybigfile.sql.gz' | pv | gzip -cd | mysql -uroot -p dbname

# taking a snapshot of a btrfs partition
btrfs send /snapshots/$date | pv | btrfs receive /mnt/backup/root

Tôi không biết bất kỳ lệnh nào cung cấp các thanh tiến trình một dòng theo kiểu pvhoặc wget, nhưng có rất nhiều tập lệnh Bash / Perl / sed đơn giản sẽ thêm chức năng đó, như những người khác đã chia sẻ ở đây.


Để hiển thị quá trình của một vòng lặp với pvbạn, bạn có thể làm cho nó tìm kiếm đầu ra của vòng lặp hoặc tạo một số đầu ra giả, ví dụ như echotrong mỗi lần lặp, đưa nó vào pvvà đưa ra số lần lặp với -s. Nếu không muốn, hãy nhớ chuyển hướng thiết bị xuất chuẩn của vòng lặp sang /dev/null. Đây là một ví dụ cho thấy cách tiếp cận này .
tráng miệng

6

Bạn có thể sử dụng zenityđể tạo các cửa sổ hộp thoại GTK đơn giản. Một trong các tùy chọn có sẵn là một hộp thoại thanh tiến trình.

Bạn tạo một cửa sổ như vậy bằng cách sử dụng zenity --progress. Để làm cho nó hữu ích, bạn nên chỉ định thêm thông tin bằng cách thêm một số tùy chọn bên dưới (trích từ man zenity):

   Progress options
   --text=STRING
          Set the dialog text
   --percentage=INT
          Set initial percentage
   --auto-close
          Close dialog when 100% has been reached
   --auto-kill
          Kill parent process if cancel button is pressed
   --pulsate
          Pulsate progress bar
   --no-cancel
          Hides the cancel button

Có hai chế độ:

  • Xung : Thanh tiến trình đang dao động, nó chỉ cho biết có gì đó đang chạy, nhưng không cho biết gì về tiến trình. Bạn làm điều này bằng cách thiết lập --pulsatingtùy chọn.

  • thủ công : Bạn phải chuyển tỷ lệ phần trăm tiến trình hiện tại sang zenityđầu vào tiêu chuẩn của lệnh để cập nhật thanh tiến trình.
    Một ví dụ cho điều này có thể trông như thế dưới đây. Lưu ý rằng các lệnh trước được nhóm thành một lớp con để tất cả đầu ra được chuyển hướng đến zenityhộp thoại và không chỉ các lệnh cuối cùng:

    (echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress

Chỉ trong trường hợp này cũng sẽ là một lựa chọn.
Chỉ huy Byte

1
Xin lỗi bạn thân mến, đây là cửa sổ thanh tiến trình GUI, tôi muốn tạo một thanh tiến trình trong thiết bị đầu cuối, ví dụ tôi muốn xem nó trong khi tập lệnh đang kiểm tra ==>[ ###########--------------] 52%
Black Hawk

1
Tôi hiểu. Chỉ là tôi đã viết một nửa câu trả lời của mình khi bạn nói điều đó, vì vậy tôi chỉ quyết định đăng nó bằng mọi cách trong trường hợp ai đó có thể cần nó trong tương lai. Tôi hy vọng bạn không phiền, vì có khá nhiều giải pháp dựa trên thiết bị đầu cuối là tốt.
Chỉ huy Byte

5

Mã này sẽ làm điều đó và không yêu cầu bất cứ điều gì (trừ bash, tất nhiên). Nó in #các dấu hiệu, giống như bạn hỏi trong bình luận của bạn:

pass='number1 number12 number13 number14 number15 number16'
chk='number14'
passarr=($pass)
lenProgressBar=${#passarr[@]}

echo -n '['
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -n '-'
    ((i++))
done

echo -n ']'
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -e -n '\b'
    ((i++))
done

echo -e -n '\b'
for i in $pass ; do
    if [ "$i" = "$chk" ]; then
        echo -e '#\nFound ^_^'
        break
    else
        echo -n '#'
    fi
done

Tuy nhiên, nếu bạn có nhiều thứ để kiểm tra, điều này sẽ chỉ lấp đầy màn hình của bạn bằng #các dấu hiệu. Để khắc phục sự cố đó, hãy thử mã này:

lenProgressBar=5
pass='number1 number12 number13 number14 number15 number16'
chk='number14'
passarr=($pass)
lenPass=${#passarr[@]}

if [ $lenProgressBar -gt $lenPass ]; then
    lenProgressBar=lenPass
elif [ $lenProgressBar -lt 1 ]; then
    lenProgressBar=1
fi

let "chksForEqualsPrint = $lenPass / $lenProgressBar"
echo -n '['
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -n '-'
    ((i++))
done

echo -n ']'
i=0

while [ $i -lt $lenProgressBar ]; do
    echo -e -n '\b'
    ((i++))
done

echo -e -n '\b'
n=1

for i in $pass ; do
    if [ "$i" = "$chk" ]; then
        echo -e '\nFound ^_^'
        break
    else
        if [ $n -eq $chksForEqualsPrint ]; then
            echo -n '#'
            n=1
        else
            ((n++))
        fi
    fi
done

Thay đổi 5 trong dòng đầu tiên ( lenProgressBar=5) thành độ dài mà bạn muốn thanh tiến trình của bạn là. Sẽ mất nhiều thời gian hơn để in một #dấu hiệu với các thanh tiến trình có chiều dài thấp hơn so với các thanh có độ dài cao hơn, nhưng đừng để chiều dài của thanh tiến trình vượt quá kích thước màn hình của bạn; nó sẽ không hoạt động tốt nếu bạn làm. (Nó sẽ không cho phép bạn sử dụng thanh tiến trình cao hơn số lượng mục bạn đang kiểm tra hoặc thấp hơn 1)


1
Bạn có thể sử dụng tput colsđể phát hiện chiều rộng của cửa sổ đầu cuối và chia tỷ lệ thanh tiến trình tương ứng.
Mikkel

1

Đây là một cách tiếp cận khác sử dụng mã thoát ansi:

#!/bin/bash

pass='number1 number2 number 3 number4 number12 number13 number14 number15 number16'
chk='number15'
result="Not Found!"

echo
echo -n "Working... "
echo -ne "\033[1;32m\033[7m\033[?25l"

for i in $pass ; do
   sleep .4s
   if [ "$i" == "$chk" ]; then
      result="  Found ^_^"
      break
   else
      echo -n " "
   fi
done

echo -ne "\r\033[0m\033[K\033[?25h"
echo $result
echo
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.