Подтвердить что ты не робот

Spinner в Shell

Я нашел этот для BASH, но я хочу сделать то же самое с оболочкой (#!/bin/sh).

Твист должен также сделать его таймером, так что подождите 60 секунд, пока он не закончится.

4b9b3361

Ответ 1

собственное решение Fadi полезно (и поставляется с любезностью комментария Адама Каца о связанном ответе), но поставляется с двумя оговорками:

  • Счетчик, благодаря использованию \r, работает только в начале строки.
  • Per POSIX, sleep поддерживает только целые секунды.

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

Следующие фрагменты устраняют эти проблемы; они используют \b (backspace), а не \r, что позволяет отображать счетчик с предыдущим текстом:


Асинхронный случай (опрос для завершения):

Если вы ожидаете завершения процесса асинхронно (периодически проверяя завершение в цикле):

printf 'Processing: '
while :; do
  for c in / - \\ \|; do # Loop over the sequence of spinner chars.
    # Print next spinner char.
    printf '%s\b' "$c"
    # Perform your custom test to see if the operation is done here.
    # In this example we wait for a file named 'results' to appear.
    # Note that `[ -f results ] && ...` is just a shorter alternative to
    # `if [ -f results]; then ... fi`.
    [ -f results ] && { printf '\n'; break 2; } # Print a newline, break out of both loops.
    sleep 1 # Sleep, then continue the loop.
  done
done

Вышеупомянутое, из-за печати \b char. после spinner char., отображает курсор за счетчиком char; если это эстетически нежелательно, используйте следующий вариант: отобразите курсор над вертушкой:

printf 'Processing:  ' # note the extra space, which will be erased in the first iteration
while :; do
  for c in / - \\ \|; do
    printf '\b%s' "$c" 
    [ -f results ] && { printf '\n'; break 2; }
    sleep 1
  done
done

I0_ol предлагает использовать tput civis и tput cnorm для временно скрывать курсор; в то время как не строго POSIX-совместимый (POSIX задает только 3 tput операнды: clear, init и reset), он, по-видимому, поддерживается большинством современных терминальных эмуляторов.

printf 'Processing: '
tput civis # Hide cursor.
# To be safe, ensure that the cursor is turned back on when
# the script terminates, for whatever reason.
trap 'tput cnorm' EXIT
while :; do
  for c in / - \\ \|; do
    printf '%s\b' "$c" 
    [ -f results ] && { printf '\n'; break 2; }
    sleep 1
  done
done
tput cnorm # Show cursor again.

Более полный пример с настраиваемым тайм-аутом и интервалом ожидания (обратите внимание, что время ожидания не будет точным, так как время, затрачиваемое на обработку каждой итерации цикла, не учитывается, в bash, вы можете просто reset специальный var. SECONDS до начала цикла, а затем проверить его значение):

# Determine how long to sleep in each iteration
# and when to timeout (integral seconds).
sleepInterval=1 timeout=10 elapsed=0 timedOut=0

printf 'Processing:  ' # note the extra space, which will be erased in the first iteration
while :; do
  for c in / - \\ \|; do
    printf '\b%s' "$c" 
    [ -f results ] && { printf '\nDone.\n'; break 2; }
    [ $elapsed -ge $timeout ] && { timedOut=1; printf '\nTIMED OUT\n' >&2; break 2; }
    sleep $sleepInterval
    elapsed=$(( elapsed + sleepInterval ))
  done
done

Синхронный (блокирующий) случай:

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

printf 'Processing: '
# Start the spinner in the background.
# The background job PID is stored in special variable `$!`.
(while :; do for c in / - \\ \|; do printf '%s\b' "$c"; sleep 1; done; done) &

# Run the synchronous (blocking) command.
# In this example we simply sleep for a few seconds.
sleep 3

# The blocking command has finished:
# Print a newline and kill the spinner job.
{ printf '\n'; kill $! && wait $!; } 2>/dev/null

echo Done.

Ответ 2

Решение таково:

#!/bin/sh

i=0
while [ $i -le 60 ]; do
  for s in / - \\ \|; do
    printf "\r$s"
    sleep .1
  done
  i=$((i+1))
done