Як додати смужку прогресу до сценарію оболонки?


413

Під час написання сценарію в bash або будь-якій іншій оболонці в * NIX під час виконання команди, яка займе більше декількох секунд, потрібна смужка прогресу.

Наприклад, копіювання великого файлу, відкриття великого файлу tar.

Якими способами ви рекомендуєте додавати смужки прогресу до скриптів оболонки?


Дивіться також stackoverflow.com/questions/12498304/… для прикладів логіки управління (виконайте завдання та виконайте щось, поки воно не закінчиться).
тріплей

1
Існує набір вимог, які ми часто знаходимо корисними під час створення сценаріїв. ведення журналів, показ прогресу, кольорів, фантазійних результатів тощо ... Я завжди вважав, що має бути якась проста рамка сценаріїв. Нарешті я вирішив реалізувати його, оскільки не зміг знайти жодного. Вам це може бути корисним. Це в чистому баші, я маю на увазі Just Bash. github.com/SumuduLansakara/JustBash
Сумуду

Чи не слід це переміщувати на 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
Джастін Дженкінс

51
Портативний спосіб вивести це - використовувати printfзамість echo.
Єнс

9
для printf нам слід було б використовувати цей формат: printf "#### (50%%)\r"він не працюватиме з одинарними котируваннями, а знаком відсотків потрібно уникати.
Нуреттін

7
Я цього не розумію - прийнято і купа грошей на хак "Я здогадаюся, скільки часу ця операція займе невідоме обладнання"? pv - правильна відповідь ІМО (але і бар теж зробить)
Стівен

19
Питання було "Як зробити панелі прогресу" з прикладом копіювання файлів. Я зосередився на проблемі "графіки", а не на обчисленні того, наскільки далеко йде операція копіювання файлу.
Мітч Хейле

73

Можливо, вас також зацікавить, як зробити прядильник :

Чи можу я зробити спінер на Баші?

Звичайно!

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

Щоразу, коли цикл повторюється, він відображає наступний символ у рядку sp, обертаючись навколо, коли він доходить до кінця. (i - положення поточного символу для відображення, а $ {# sp} - довжина рядка sp).

Рядок \ b замінюється символом "backspace". Крім того, ви можете грати з \ r, щоб повернутися до початку рядка.

Якщо ви хочете, щоб він уповільнився, покладіть команду сну всередину циклу (після 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може знадобитися вставки, а не десяткові)
Адам Кац,

1
@Daenyth. Дякую. Будь ласка, де нам слід викликати команду, яку нам потрібно спостерігати, як це прогрес за допомогою попереднього коду?
goro

@goro: у some_work ...рядку вище; більш детальну дискусію, яка ґрунтується на цій корисній відповіді та корисному коментарі Адама Каца - з акцентом на відповідність POSIX - можна знайти тут .
mklement0

@AdamKatz: Це корисне, портативне спрощення, але для того, щоб відповідати підходу Даєніта, спінер повинен базуватися не на \bтому \r, як він інакше працює лише на самому початку рядка: while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done- або, якщо відображатиме курсор за спінером небажано:printf ' ' && while :; do for c in / - \\ \|; do printf '\b%s' "$c"; sleep 1; done; done
mklement0

1
@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
Адам Кац

48

Деякі пости показали, як відображати виконання команди. Для того, щоб його обчислити, вам потрібно буде побачити, наскільки ви прогресували. У системах BSD деякі команди, такі як dd (1), приймають SIGINFOсигнал і повідомляють про свій хід. У системах Linux деякі команди реагують аналогічно 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/b0ab6a6aa8e1eeaf236b
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? Я збентежений.
Чираг

Замість використання sed, я використовую bash внутрішній "Заміна підрядків", оскільки це легка робота, я вважаю за краще використовувати внутрішні функції bash для такого роду роботи. Код виглядає також приємніше. :-) Перевірте тут tldp.org/LDP/abs/html/string-manipulation.html та знайдіть заміну підрядків.
боязнь

і $ {_ fill} призначається як пробіли $ {_ done}. Це прекрасно. Чудова людина. Я напевно буду використовувати це у всіх моїх сценаріях баш-ха-ха
Chirag

Чудова робота @fearside! Я трохи змінився, щоб пропустити, коли _progress не змінився від останнього значення, щоб покращити швидкість. github.com/enobufs/bash-tools/blob/master/bin/progbar
enobufs

Солодке. Зміна тире прямокутника надає йому більш професійний вигляд:printf "\rProgress : [${_fill// /▇}${_empty// / }] ${_progress}%%"
Мехді ЛАМРАНІ

44

використовуйте команду linux pv:

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

він не знає розмір, якщо він знаходиться в середині потоку, але він дає швидкість і загальний результат, і звідти ви можете зрозуміти, скільки часу має тривати, і отримати зворотній зв'язок, щоб ви знали, що він не висів.


32

Я шукав щось більш сексуальне, ніж обрана відповідь, так і зробив власний сценарій.

Попередній перегляд

progress-bar.sh в дії

Джерело

Я кладу це на 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? Я маю на увазі, як можна виміряти терміни оновлення (наприклад) на віддалених серверах?
безликий

1
@faceless це не входить до цього коду, ви надаєте час і це відраховується
Édouard Lopez

1
@Fusion - це символ унікоду (U + 2587 БІЛЬШИЙ БІЛЬСЬКИЙ БІЛЬСЬКИЙ ВІСНИК), який повинен бути безпечним для сучасної оболонки. Спробуйте спробувати свої околиці
Едуард Лопес

18

GNU tar має корисну опцію, яка забезпечує функціональність простої панелі прогресу.

(...) Ще одна дія пункту пропуску - "крапка" (або "."). Він дає команду tar для друку однієї точки у стандартному потоці лістингу, наприклад:

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

Такий же ефект може бути отриманий:

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

+1 для найпростішого підходу! Якщо ви не бачите жодних крапок, спробуйте зменшити кількість, наприклад --checkpoint=.10. Він також чудово працює при видобутку з tar -xz.
Ноам Манос

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" - повернення вагона, спеціальний знак для повернення на початок рядка

Ви можете змусити його виводити будь-який вміст з будь-якою швидкістю, тому цей метод є дуже універсальним, наприклад, часто використовується для візуалізації "злому" в нерозумних фільмах, не жартуючи.

Повна відповідь

Основна проблема полягає в тому, як визначити $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 - вбудований для друку матеріалів у заданому форматі
  • 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 (не порушує нормальний вихід)

введіть тут опис зображення

EDIT: Для оновленої версії перевірте мою сторінку github

Я не був задоволений відповідями на це питання. Те, що я особисто шукав, - це химерна смуга прогресу, як це бачить APT.

Я подивився вихідний код C для APT і вирішив написати власний еквівалент bash.

Цей рядок прогресу буде добре розташовуватися в нижній частині терміналу і не заважатиме жодному виводу, що надсилається до терміналу.

Зверніть увагу, що наразі смужка фіксується шириною 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

Я думаю, ідея полягає в тому, щоб ви поставили це в сценарій, тож це призведе до пастки лише виходу з цього сценарію.
Ігуананавт

1
Я люблю цю команду, я використовую її у своїх файлах. Мені просто неприємно, оскільки я не дуже розумію, як це працює. Перший і третій рядки легше зрозуміти, але я все ще не впевнений. Я знаю, що це стара відповідь, але чи є спосіб, коли я можу отримати інше пояснення, спрямоване на новачків при програмуванні
Феліпе

1
Це ТІЛЬКИ правдива відповідь, де інші - це лише Сценарій 101 іграшкових барів прогресу, які нічого не означають і не корисні для реальних, разових, нездійсненних (майже ВСІХ) програм. Дякую.
bekce

@Felipe, цикл while - це фоновий процес. $! у першій пастці фіксує ідентифікатор процесу цього фонового процесу та гарантує, що якщо поточний / батьківський процес закінчиться, фоновий процес теж загине, і він не залишиться висить. Заява 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_sizeа значення струму подається деякою функцією і викликається uploaded_bytes.

# Uploading a file
file_size=137921

while true; do
  # Get current value of uploaded bytes
  uploaded_bytes=$(some_function_that_reports_progress)

  # Draw a progress bar
  draw_progress_bar $uploaded_bytes $file_size "bytes"

  # Check if we reached 100%
  if [ $uploaded_bytes == $file_size ]; then break; fi
  sleep 1  # Wait before redrawing
done
# Go to the newline at the end of upload
printf "\n"

Акуратна і проста функція. Дуже дякую!
Андреас Крафт

Це те, що я шукаю! Велике спасибі :)
wajdi_jurry

4

Більшість команд unix не дасть вам прямого відгуку, з якого ви можете це зробити. Деякі дадуть вам вихід на stdout або stderr, який ви можете використовувати.

Для чогось типу tar можна використовувати перемикач -v і передавати висновок програмі, яка оновлює невелику анімацію для кожного прочитаного рядка. Як tar виписує список файлів, це розплутано, програма може оновити анімацію. Щоб виконати відсоток, вам доведеться знати кількість файлів і рахувати рядки.

CP не дає такого виду, наскільки я знаю. Щоб стежити за ходом cp, вам слід було б стежити за вихідними та цільовими файлами та стежити за розміром пункту призначення. Ви можете написати невелику програму c, використовуючи системний виклик stat (2), щоб отримати розмір файлу. Це буде читати розмір джерела, а потім запитувати цільовий файл і оновлювати% повну смугу на основі розміру файлу, записаного на сьогоднішній день.


4

У моєму рішенні відображається відсоток тарболу, який наразі не стискається та записується. Я використовую це, коли виписую зображення кореневої файлової системи 2 Гб. Вам справді потрібна планка прогресу для цих речей. Те, що я роблю, - це використання gzip --list це отримати загальний нестиснений розмір тарболу. З цього я обчислюю коефіцієнт блокування, необхідний для поділу файлу на 100 частин. Нарешті, я друкую повідомлення про контрольну точку для кожного блоку. Для файлу об'ємом 2 Гб це близько 10 МБ блоку. Якщо це занадто велика кількість, ви можете розділити 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}
}

Гарне рішення, але як це робити, коли потрібно стиснути каталог?
Самір Садек

4

Перш за все, бар - це не єдиний метр прогресу труби. Інший (можливо, навіть більш відомий) - pv (переглядач труб).

По-друге, бар і pv можна використовувати, наприклад, так:

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

або навіть:

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

один корисний трюк, якщо ви хочете використовувати бар і pv в командах, які працюють з файлами, наведеними в аргументах, наприклад, копіювати file1 file2, - це використовувати підстановку процесу :

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

Заміна процесу - це чарівна річ, яка створює тимчасові файли файлів труб / dev / fd / і з'єднує stdout з запущеного процесу (всередині дужок) через цю трубку і копія бачить його як звичайний файл (за одним винятком, він може читати лише його вперед).

Оновлення:

Сама команда bar дозволяє також копіювати. Після людини бар:

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

Але заміщення процесу - на мій погляд, більш загальний спосіб зробити це. Він використовує саму програму cp.


3

Я вважаю за краще використовувати діалог із --gauge . Використовується дуже часто в .deb-пакунках та інших елементах конфігурації багатьох дистрибутивів. Тож вам не потрібно винаходити колесо ... знову

Просто поставте значення 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буде видавати кожне ім'я файлу під час його стискання), то існує більш просте, швидке, безболісне та настроюване рішення.

Встановіть модуль 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

Допомога: tqdm -h. Приклад використання додаткових варіантів:

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

В якості бонусу ви також можете використовувати tqdmобернути ітерабелі в код python.

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

На основі роботи Едуарда Лопеса я створив панель прогресу, яка відповідає розміру екрана, який би він не був. Перевір.

введіть тут опис зображення

Він також розміщений на 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

Це стосується лише використання znity gnome. Zenity надає чудовий інтерфейс для bash-скриптів: https://help.gnome.org/users/zenity/stable/

Приклад із Zenity Progress Bar:

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

Я використовував відповідь зі створення рядка повторюваних символів у скрипті оболонки для повторення чару. У мене є дві відносно невеликі версії bash для скриптів, для яких потрібно відображати панель прогресу (наприклад, цикл, який проходить через багато файлів, але не корисний для великих файлів 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

Якщо вам потрібна >&2панель прогресу на stderr, просто додайте в кінці кожної команди 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;

Можна використовувати прапори / змінні всередині циклу while, щоб перевірити та відобразити значення / ступінь прогресу.


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))"
Серхіо

0

Я зробив чисту версію оболонки для вбудованої системи, використовуючи:

  • Функція обробки сигналу SIGUSR1 / usr / bin / dd

    В основному, якщо ви надішлете "убити SIGUSR1 $ (pid_of_running_dd_process)", він виведе зведення про швидкість пропускання та передану кількість.

  • backgrounding dd, а потім регулярно запитувати його для оновлень та генерувати хеш-тики, як раніше використовувались ftp-клієнти старої школи.

  • Використання / dev / stdout в якості місця призначення для нестандартних програм, таких як scp

Кінцевий результат дозволяє вам здійснити будь-яку операцію передачі файлів і отримати оновлення прогресу, схоже на вихідний хеш-код 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, можна показати панель прогресу зі свого скрипта bash, виконавши наступну команду:

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" на прогнозований розмір вихідного файлу)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.