外壳微调器 [英] Spinner in Shell

查看:98
本文介绍了外壳微调器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于BASH,我发现了 ,但是我想对shell(#!/bin/sh)做同样的事情. /p>

不同之处在于也将其设置为计时器,例如,等待60秒直到结束.

解决方案

Fadi自己的解决方案很有帮助(由由 Adam Katz对链接答案的评论提供),但有两个警告:

  • 由于使用了\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.

具有可配置超时和睡眠间隔的更完整示例 (请注意,由于未考虑处理每个循环迭代所花费的时间,因此超时实施并不准确; >,您可以在循环开始之前简单地重置特殊变量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.

I found this for BASH, but I want to do the same thing with shell (#!/bin/sh).

The twist is to make it a timer also, so like wait 60 seconds for example until it ends.

解决方案

Fadi's own solution is helpful (and comes by courtesy of Adam Katz's comment on the linked answer), but comes with 2 caveats:

  • The spinner, due to using \r, only works at the beginning of a line.
  • Per POSIX, sleep only supports integral seconds.

It may also not be readily obvious where to test for whether the operation is done and how to exit the two loops vs. how to use a spinner as a background job, while waiting for a blocking command to finish.

The following snippets address these issues; they use \b (backspace) rather than \r, which allows the spinner to be displayed with preceding text, if desired:


Asynchronous case (poll for completion):

If you're waiting for completion of a process asynchronously (by checking for completion periodically, in a loop):

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

The above, due to printing the \b char. after the spinner char., displays the cursor behind the spinner char; if that is aesthetically undesirable, use the following variation to display the cursor on top of the spinner:

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 suggests using tput civis and tput cnorm to hide the cursor temporarily; while not strictly POSIX-compliant (POSIX mandates only 3 tput operands: clear, init, and reset), it does appear to be supported by most modern terminal emulators.

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.

A more complete example with configurable timeout and sleep interval (note that the timeout enforcement will not be exact, as the time it takes to process each loop iteration is not taken into account; in bash, you could simply reset special var. SECONDS before the loop starts and then check its value):

# 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


Synchronous (blocking) case:

If you're waiting for a lengthy synchronous (blocking) command to complete, the spinner must be launched as a background job, which you then terminate once the blocking call has completed.

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.

这篇关于外壳微调器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆