Shell 中的微调器

Spinner in Shell

我为 BASH 找到了 this,但我想对 shell (#!/bin/sh) 做同样的事情。

扭曲是让它也成为一个计时器,例如等待 60 秒直到它结束。

解决方法是这样的:

#!/bin/sh

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

is helpful (and comes by courtesy of Adam Katz's 评论链接的答案),但有 2 个警告:

  • 微调器,由于使用 \r,仅在一行的 开头 起作用。
  • 每POSIX,sleep只支持积分秒。

在何处测试操作是否完成以及如何退出两个循环与如何将微调器用作 background 作业可能也不是很明显,在等待阻塞命令完成时。

以下片段解决了这些问题;他们使用 \b(退格键)而不是 \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 个字符。 微调字符之后,显示光标 微调字符之后;如果这在美学上不合需要,请使用以下变体 在微调器 的顶部 显示光标 :

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 civistput cnorm暂时隐藏光标;虽然不严格符合 POSIX(POSIX 仅要求 3 个 tput 操作数:clearinitreset),但它似乎确实得到大多数现代终端仿真器的支持。

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 中,您可以在循环开始之前简单地重置特殊变量 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's 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.