Ответ 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.