Как добавить индикатор выполнения в сценарий оболочки?

Вышеупомянутые примечания верны, но имейте в виду, что пользователь может заставить свой браузер не отправлять эту информацию, или они могут связываться с этой информацией и отправлять ложные данные.

368
задан Jens 30 May 2012 в 10:56
поделиться

36 ответов

Вы можете реализовать это, переписав строку. Используйте \r, чтобы вернуться к началу строки, не записывая \n в терминал.

Напишите \n, когда закончите продвигать линию.

Используйте echo -ne, чтобы:

  1. не печатать \n и
  2. для распознавания escape-последовательностей, подобных \r.

Вот демо:

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

В комментарии ниже, puk упоминает, что это «не получается», если вы начинаете с длинной строки, а затем хотите написать короткую строку: в этом случае, вам нужно будет перезаписать длину длинной строки (например, пробелами).

651
ответ дан Mitch Haile 30 May 2012 в 10:56
поделиться

Чтобы указать ход выполнения действий, попробуйте следующие команды:

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
ответ дан S471 30 May 2012 в 10:56
поделиться

Используя предложения, перечисленные выше, я решил реализовать свой собственный индикатор выполнения.

#!/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
ответ дан Qub3r 30 May 2012 в 10:56
поделиться

Основываясь на работе Эдуарда Лопеса, я создал индикатор выполнения, который соответствует размеру экрана, каким бы он ни был. Проверьте это.

enter image description here

Он также размещен на 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: [110] [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
ответ дан Adriano_epifas 30 May 2012 в 10:56
поделиться

https://github.com/extensionsapp/progre.sh

Создание 40-процентного прогресса: progreSh 40

enter image description here

1
ответ дан Hello World 30 May 2012 в 10:56
поделиться

Если вам нужно показать временную шкалу прогресса (заранее зная время показа), вы можете использовать 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
ответ дан auino 30 May 2012 в 10:56
поделиться
#!/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
ответ дан Mike Q 30 May 2012 в 10:56
поделиться

Я сделал чисто версию оболочки для встроенной системы, используя преимущества:

  • / usr / bin / dd SIGUSR1 для обработки сигналов.

    По сути, если вы отправите 'kill SIGUSR1 $ (pid_of_running_dd_process)', он выведет сводную информацию о скорости передачи и количестве передаваемых данных.

  • Фоновая обработка dd, а затем регулярный запрос его на наличие обновлений и генерация тиковых хешей, как раньше использовали старые ftp-клиенты.

  • Использование / dev / stdout в качестве места назначения для дружественных к 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
0
ответ дан synthesizerpatel 30 May 2012 в 10:56
поделиться

У меня было то же самое сегодня, и, основываясь на ответе Диомидиса, вот как я это сделал (linux debian 6.0.7). Может быть, это могло бы помочь вам:

#!/bin/bash

echo "getting script inode"
inode=`ls -i ./script.sh | cut -d" " -f1`
echo $inode

echo "getting the script size"
size=`cat script.sh | wc -c`
echo $size

echo "executing script"
./script.sh &
pid=$!
echo "child pid = $pid"

while true; do
        let offset=`lsof -o0 -o -p $pid | grep $inode | awk -F" " '{print $7}' | cut -d"t" -f 2`
        let percent=100*$offset/$size
        echo -ne " $percent %\r"
done
-4
ответ дан akiuni 30 May 2012 в 10:56
поделиться

Я также хотел бы добавить свой собственный индикатор выполнения

Он достигает точности субсимволов, используя половину блоков Unicode

enter image description here

Код включен

11
ответ дан nachoparker 30 May 2012 в 10:56
поделиться

Я искал что-то более сексуальное, чем выбранный ответ, как и мой собственный сценарий.

Предварительный просмотр

progress-bar.sh in action

Источник

Я поместил его на github progress-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
23
ответ дан Édouard Lopez 30 May 2012 в 10:56
поделиться

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

#!/bin/bash
# Updates the progress bar
# Parameters: 1. Percentage value
update_progress_bar()
{
  if [ $# -eq 1 ];
  then
    if [[ $1 == [0-9]* ]];
    then
      if [ $1 -ge 0 ];
      then
        if [ $1 -le 100 ];
        then
          local val=$1
          local max=100

          echo -n "["

          for j in $(seq $max);
          do
            if [ $j -lt $val ];
            then
              echo -n "="
            else
              if [ $j -eq $max ];
              then
                echo -n "]"
              else
                echo -n "."
              fi
            fi
          done

          echo -ne " "$val"%\r"

          if [ $val -eq $max ];
          then
            echo ""
          fi
        fi
      fi
    fi
  fi
}

update_progress_bar 0
# Further (time intensive) actions and progress bar updates
update_progress_bar 100
-3
ответ дан janr 30 May 2012 в 10:56
поделиться

используйте команду linux pv:

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

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

40
ответ дан Seth Wegner 30 May 2012 в 10:56
поделиться

Более простой метод, который работает в моей системе с использованием утилиты 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
12
ответ дан leebert 30 May 2012 в 10:56
поделиться

Это применимо только с использованием гнома zenity. Zenity предоставляет отличный нативный интерфейс для скриптов bash: https://help.gnome.org/users/zenity/stable/

Из строки прогресса Zenity Пример:

#!/bin/sh
(
echo "10" ; sleep 1
echo "# Updating mail logs" ; sleep 1
echo "20" ; sleep 1
echo "# Resetting cron jobs" ; sleep 1
echo "50" ; sleep 1
echo "This line will just be ignored" ; sleep 1
echo "75" ; sleep 1
echo "# Rebooting system" ; sleep 1
echo "100" ; sleep 1
) |
zenity --progress \
  --title="Update System Logs" \
  --text="Scanning mail logs..." \
  --percentage=0

if [ "$?" = -1 ] ; then
        zenity --error \
          --text="Update canceled."
fi
1
ответ дан tPSU 30 May 2012 в 10:56
поделиться

Прежде всего, это не единственный индикатор хода трубы. Другой (может быть, даже более известный) - это pv (pipe viewer).

Во-вторых, bar и pv можно использовать, например, так:

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

или даже:

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

один полезный трюк, если вы хотите использовать бар и pv в командах, которые работают с файлами, указанными в аргументах, например, например copy file1 file2, должен использовать подстановка процесса :

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

Подстановка процесса - это волшебная вещь bash, которая создает временные файлы fifo pipe / dev / fd / и подключает стандартный вывод из запущенного процесса ( в круглых скобках) через этот канал, и копия видит его как обычный файл (за одним исключением, он может только читать его вперед).

Обновление:

сама команда bar также позволяет копировать. После man bar:

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

Но замена процесса - это, на мой взгляд, более общий способ сделать это. Он использует саму программу cp.

4
ответ дан thedk 30 May 2012 в 10:56
поделиться

Многие ответы описывают написание ваших собственных команд для распечатки '\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

2
ответ дан casper.dcl 30 May 2012 в 10:56
поделиться

Большинство команд Unix не даст вам прямой обратной связи, из которой вы можете сделать это. Некоторые дадут вам вывод на стандартный вывод или стандартный вывод, который вы можете использовать.

Для чего-то вроде tar вы можете использовать ключ -v и направить вывод в программу, которая обновляет небольшую анимацию для каждой строки, которую она читает. Когда tar записывает список файлов, которые он распаковывает, программа может обновить анимацию. Чтобы выполнить процент выполнения, вам нужно знать количество файлов и считать строки.

cp не дает такой вывод, насколько я знаю. Чтобы отслеживать прогресс cp, вам нужно будет отслеживать исходные и конечные файлы и следить за размером получателя. Вы можете написать небольшую программу на c, используя системный вызов stat (2) , чтобы получить размер файла. Это будет считывать размер источника, затем опрашивать файл назначения и обновлять панель% complete в зависимости от размера файла, записанного на сегодняшний день.

4
ответ дан ConcernedOfTunbridgeWells 30 May 2012 в 10:56
поделиться

Вы также можете быть заинтересованы в , как сделать прядильщик :

Можно ли сделать прядильщик в Bash?

Конечно!

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

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

Строка \ b заменяется символом «возврат». Или вы можете поиграть с \ 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
63
ответ дан Daenyth 30 May 2012 в 10:56
поделиться

Я хотел отследить прогресс, основываясь на количестве строк, которые выводила команда, против целевого числа строк из предыдущего запуска:

#!/bin/bash
function lines {
  local file=$1
  local default=$2
  if [[ -f $file ]]; then
    wc -l $file | awk '{print $1}';
  else
    echo $default
  fi
}

function bar {
  local items=$1
  local total=$2
  local size=$3
  percent=$(($items*$size/$total % $size))
  left=$(($size-$percent))
  chars=$(local s=$(printf "%${percent}s"); echo "${s// /=}")
  echo -ne "[$chars>";
  printf "%${left}s"
  echo -ne ']\r'
}

function clearbar {
  local size=$1
  printf " %${size}s  "
  echo -ne "\r"
}

function progress {
  local pid=$1
  local total=$2
  local file=$3

  bar 0 100 50
  while [[ "$(ps a | awk '{print $1}' | grep $pid)" ]]; do
    bar $(lines $file 0) $total 50
    sleep 1
  done
  clearbar 50
  wait $pid
  return $?
}

Затем используйте его так:

target=$(lines build.log 1000)
(mvn clean install > build.log 2>&1) &
progress $! $target build.log

Он выводит индикатор выполнения, который выглядит примерно так:

[===============================================>   ]

Индикатор растет по мере того, как количество выводимых строк достигает цели. Если количество строк превышает цель, планка начинается снова (надеюсь, цель хорошая).

Кстати: я использую bash на Mac OSX. Я основал этот код на счетчике из Мариасио .

-1
ответ дан pbatey 30 May 2012 в 10:56
поделиться

Это позволяет визуализировать, что команда все еще выполняется:

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

Это создаст бесконечный цикл while , который выполняется в фоновом режиме и отображает «». каждую секунду. Это отобразит . в оболочке. Запустите команду tar или любую другую команду. Когда эта команда завершает выполнение, то уничтожает последнее задание, работающее в фоновом режиме - это бесконечный цикл while .

7
ответ дан PSkocik 30 May 2012 в 10:56
поделиться

Я использовал ответ из Создание строки повторяющихся символов в скрипте оболочки для повторения символов. У меня есть две относительно небольшие версии 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

Если вам нужен прогрессбар на stderr, просто добавьте >&2 в конце каждой команды printf.

1
ответ дан Community 30 May 2012 в 10:56
поделиться

GNU tar имеет полезную опцию, которая дает функциональность простого индикатора выполнения.

(...) Другое доступное действие контрольной точки - «точка» (или «.»). Он указывает tar печатать одну точку в стандартном потоке листинга, например :

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

Тот же эффект может быть получен с помощью:

$ tar -c --checkpoint=.1000 /var
17
ответ дан Wojtek 30 May 2012 в 10:56
поделиться

Индикатор выполнения в стиле APT (не нарушает нормальный вывод)

enter image description here

РЕДАКТИРОВАТЬ: Для получения обновленной версии проверьте мой 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}"
}
3
ответ дан polle 30 May 2012 в 10:56
поделиться

Мое решение отображает процентную долю архива, который в данный момент распаковывается и записывается. Я использую это, когда записываю образы корневой файловой системы 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
ответ дан Steven Penny 30 May 2012 в 10:56
поделиться

Это психоделический индикатор прогресса для bash-скриптинга от nExace. Его можно вызвать из командной строки как «./progressbar x y», где «x» - это время в секундах, а «y» - это сообщение, связанное с этой частью процесса.

Внутренняя функция progressbar () сама по себе также хороша, если вы хотите, чтобы другие части вашего скрипта управляли индикатором выполнения. Например, отправив «Индикатор прогресса 10« Создание дерева каталогов »; будет отображаться:

[#######                                     ] (10%) Creating directory tree

Конечно, это будет приятно психоделический, хотя ...

#!/bin/bash

if [ "$#" -eq 0 ]; then echo "x is \"time in seconds\" and z is \"message\""; echo "Usage: progressbar x z"; exit; fi
progressbar() {
        local loca=$1; local loca2=$2;
        declare -a bgcolors; declare -a fgcolors;
        for i in {40..46} {100..106}; do
                bgcolors+=("$i")
        done
        for i in {30..36} {90..96}; do
                fgcolors+=("$i")
        done
        local u=$(( 50 - loca ));
        local y; local t;
        local z; z=$(printf '%*s' "$u");
        local w=$(( loca * 2 ));
        local bouncer=".oO°Oo.";
        for ((i=0;i<loca;i++)); do
                t="${bouncer:((i%${#bouncer})):1}"
                bgcolor="\\E[${bgcolors[RANDOM % 14]}m \\033[m"
                y+="$bgcolor";
        done
        fgcolor="\\E[${fgcolors[RANDOM % 14]}m"
        echo -ne " $fgcolor$t$y$z$fgcolor$t \\E[96m(\\E[36m$w%\\E[96m)\\E[92m $fgcolor$loca2\\033[m\r"
};
timeprogress() {
        local loca="$1"; local loca2="$2";
        loca=$(bc -l <<< scale=2\;"$loca/50")
        for i in {1..50}; do
                progressbar "$i" "$loca2";
                sleep "$loca";
        done
        printf "\n"
};
timeprogress "$1" "$2"
-1
ответ дан nexace 30 May 2012 в 10:56
поделиться

для меня проще всего пользоваться и лучше всего выглядеть до сих пор - это команда 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
2
ответ дан hyde 30 May 2012 в 10:56
поделиться

Некоторые посты показали, как отображать прогресс команды. Чтобы рассчитать это, вам нужно увидеть, насколько вы продвинулись. В системах 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 появилась в моем блоге .

48
ответ дан Diomidis Spinellis 30 May 2012 в 10:56
поделиться

Вот как это может выглядеть

Загрузка файла

[##################################################] 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 (( 

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_max < 1 )); then __max=1; fi # anti zero division protection local __percentage=$(( 100 - (

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_max*100 -

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_value*100) /

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_max )) # Rescale the bar according to the progress bar width local __num_bar=$((

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_percentage * $PROGRESS_BAR_WIDTH / 100 )) # Draw progress bar printf "[" for b in $(seq 1

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_num_bar); do printf "#"; done for s in $(seq 1 $(( $PROGRESS_BAR_WIDTH -

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_num_bar ))); do printf " "; done printf "]

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_percentage%% (

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_value /

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_max

Вот как это может выглядеть

Загрузка файла

[110]

Ожидание завершения задания

[111]

Простая функция, которая ее реализует

Вы можете просто скопировать и вставить ее в свой скрипт. Больше ничего не требуется для работы.

[112]

Пример использования

Здесь мы загружаем файл и перерисовываем индикатор выполнения на каждой итерации. Неважно, какое задание фактически выполняется, если мы можем получить 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"
_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"
4
ответ дан Vagiz Duseev 30 May 2012 в 10:56
поделиться

Ничего подобного не видел, так что ... мое очень простое решение:

#!/bin/bash
BAR='####################'   # this is full bar, mine is 20 chars
for i in {1..20}; do
    echo -ne "\r${BAR:0:$i}" # print $i chars of $BAR from 0 position
    sleep .1
done
  • echo -n - печатать без новой строки в конце
  • echo -e - интерпретировать специальные символы при печати
  • "\r" - возврат каретки, специальный символ для возврата в начало строки

Я использовал его давным-давно в простом «взломе» видео "для имитации набора текста. ;)

8
ответ дан cprn 30 May 2012 в 10:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: