Создать индикатор выполнения в Bash

13

Как я могу создать индикатор выполнения с помощью bash?

Это мой сценарий:

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

Я хочу заменить echo 'loading 50%'что-нибудь, чтобы создать индикатор выполнения.

Черный ястреб
источник
1
Индикатор выполнения в терминале или индикатор выполнения в отдельном окне графического интерфейса?
Byte Commander
2
В терминале
Black Hawk
etaМожет делать то , что вы хотите.
aioobe

Ответы:

14

whiptail поставляется предустановленным в Ubuntu и многих других дистрибутивах и будет отображать полноэкранные (но все еще основанные на терминалах) элементы прогресса.

dialogявляется надмножеством whiptail, так что этот пример будет одинаково хорошо работать с обоими. Он предоставляет более продвинутые элементы пользовательского интерфейса, поэтому он может пригодиться, если вы ищете взаимодействие с пользователем, такое как средства выбора файлов и формы, но его недостатком является то, что он не предустанавливается во многих системах.

Хлыстохвост

диалог

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

Обратите внимание, что вывод сценария интерпретируется в процентах, поэтому вам, возможно, придется соответствующим образом настроить вывод.

Whiptail и Dialog также позволяют вам изменять текст во время выполнения с помощью довольно загадочного синтаксиса:

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

pvпоказывает прогресс файла или потока, проходящего через него. Однако его нельзя (легко?) Использовать для отображения прогресса пользовательской операции, такой как цикл. Он разработан специально для потоков.

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

Некоторые примеры из реальной жизни, где это pvпригодится:

# 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

Я не знаю ни одной команды, которая выдает однострочные индикаторы выполнения в стиле pvили wget, но есть много простых скриптов Bash / Perl / sed, которые добавят эту функциональность, как и другие здесь поделились.

Миккель
источник
Чтобы показать процесс цикла с pvвами, вы можете либо заставить его искать выходные данные цикла, либо создать какой-то фальшивый вывод, например, echoв каждой итерации, направить его pvи передать ему счетчик итераций -s. Если он не нужен, не забудьте перенаправить стандартный вывод цикла /dev/null. Вот пример, показывающий этот подход .
десерт
6

Вы можете использовать zenityдля создания простых диалоговых окон GTK. Одним из доступных вариантов является диалоговое окно индикатора выполнения.

Вы создаете такое окно, используя zenity --progress. Чтобы сделать его полезным, вы должны указать дополнительную информацию, добавив некоторые из параметров ниже (выдержка из 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

Есть два режима:

  • пульсирующий : индикатор выполнения пульсирующий, он просто указывает на то, что что-то работает, но ничего не говорит о прогрессе. Вы делаете это, устанавливая --pulsatingопцию.

  • manual : необходимо zenityобновить текущий процент выполнения для стандартного ввода команды, чтобы обновить индикатор выполнения.
    Пример для этого может выглядеть следующим образом. Обратите внимание, что предыдущие команды сгруппированы в подоболочку, так что весь вывод перенаправляется в zenityдиалог, а не только в последнюю команду:

    (echo 10; sleep 2; echo 20; sleep 2; echo 50; sleep 2) | zenity --progress
Byte Commander
источник
На всякий случай это тоже будет вариант.
Byte Commander
1
Извините, дорогой, это окна с индикатором прогресса графического интерфейса, я хочу создать индикатор выполнения в терминале, например, я хочу видеть его во время проверки скрипта ==>[ ###########--------------] 52%
Black Hawk
1
Да, я понимаю. Просто я уже написал половину своего ответа, когда вы сказали это, поэтому я решил в любом случае опубликовать его на тот случай, если кому-то еще это понадобится в будущем. Надеюсь, вы не возражаете, поскольку существует также немало терминальных решений.
Byte Commander
5

Этот код сделает это, и ничего не требует (кроме bash, конечно). Он печатает #знаки, как вы и просили в своем комментарии:

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

Однако, если вам есть что проверить, это просто заполнит ваш экран #знаками. Чтобы решить эту проблему, попробуйте этот код:

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

Измените 5 в первой строке ( lenProgressBar=5) на длину, которую вы хотите, чтобы ваш индикатор выполнения был. Печатается #знак с полосами выполнения меньшей длины, чем с полосами большей длины, но не позволяйте длине индикатора выполнения превышать размер экрана; это не будет работать хорошо, если вы делаете. (Это не позволит вам использовать индикатор выполнения больше, чем количество проверяемых элементов или меньше 1)

insert_name_here
источник
1
Вы можете использовать tput colsдля определения ширины окна терминала и соответственно масштабировать индикатор выполнения.
Миккель
1

Вот еще один подход с использованием escape-кодов 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
bashBedlam
источник