多个进度条

发布于 2024-08-24 15:27:04 字数 228 浏览 3 评论 0原文

除了我之前关于 shell 脚本中的多线程的查询之外,我很好奇是否可以有多个进度条。

这是我预期结果的代码片段:

Output : 1 of 100  Files Completed   # Thread1
Output : 10 of 45 files Completed     # Thread 2    

这些行已更新,显示进度。可以在shell中实现吗?

In addition to my previous query concerning multi-threading in shell scripting, I am curious if its possible to have multiple progress bar.

Here is a code snippet of my expected result :

Output : 1 of 100  Files Completed   # Thread1
Output : 10 of 45 files Completed     # Thread 2    

The lines are updated showing the progress. Would it be possible to implement it in shell?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

盗琴音 2024-08-31 15:27:05

如果您的子进程输出是可过滤的,那么您可以将每个子进程的标准输出重定向到不同的文件描述符,然后调用一个函数来循环该文件描述符的输出。然后您可以尝试使用 printf 来更新进度表。但这就是你会遇到麻烦的地方。不确定 Bash 的内置 printf 是否可以处理终端的细节。我从未尝试过在 Bash 中编写一个进行多行输出的进度表。为什么不使用对话框?

If your subprocess output is filterable then you can redirect stdout of each subproc to a different file descriptor and then call a function to loop through that file descriptor's output. Then you can try to use printf to update the progress meter. This is where you will run into trouble though. Not sure Bash's builtin printf can handle the terminal to that detail. I never tried to write a progress meter in Bash that does multi-line output. Why not use dialog?

旧城烟雨 2024-08-31 15:27:04

是的,这很有可能。

假设您现有的代码(基于您之前的帖子)当前是这样的:

do_something() {
  ...
  echo -ne "\r$index of $No_of_Files Completed"
  ...
}

do_something A &
do_something B &
do_something C &
wait

...那么您可以执行以下调整以实现您想要的效果:

# Background tasks will no longer write directly to the console; instead, 
#  they will write to temporary files which will be read periodically 
#  by a special log printer task (which will display everything nicely.) 
# 
# Name of temporary files 
STATUS_BASENAME="/tmp/~$.status" 
# Process IDs of backgrounded tasks; we record them so we can wait on them 
#  specifically but not wait on the special log printer task 
TASK_PIDS="" 

do_something() { 
  # First parameter must be a task ID starting at 0 incremented by 1 
  TASK_ID=$1 ; shift 
  ...
  # We write new status to status file (note we don't echo -n, we want that 
  #  trailing newline) 
  # Try to go through a temporary status file which we rename afterwards to 
  #  avoid race conditions with the special log printer task 
  echo "$x of 5 Completed" >"${STATUS_BASENAME}.${TASK_ID}.tmp" 
  mv "${STATUS_BASENAME}.${TASK_ID}.tmp" "${STATUS_BASENAME}.${TASK_ID}" 
  ...
} 

# Special log printer task 
status_printer() { 
  # First time in the loop is special insofar we don't have to 
  #  scroll up to overwrite previous output. 
  FIRST_TIME=1 
  while true ; do 
    # If not first time, scroll up as many lines as we have 
    #  regular background tasks to overwrite previous output. 
    test $FIRST_TIME -eq 0 && for PID in $TASK_PIDS ; do 
      echo -ne '\033M' # scrol up one line using ANSI/VT100 cursor control sequences 
    done 
    FIRST_TIME=0
    TASK_ID=0
    for PID in $TASK_PIDS ; do 
      # If status file exists print first line 
      test -f "${STATUS_BASENAME}.${TASK_ID}" && head -1 "${STATUS_BASENAME}.${TASK_ID}" || echo "waiting..." 
      TASK_ID=`expr $TASK_ID + 1` # using expr for portability :) 
    done 
    test -f "${STATUS_BASENAME}.done" && return
    sleep 1 # seconds to wait between updates 
  done 
} 

do_something 0 A &
 TASK_PIDS="$TASK_PIDS $!" 
do_something 1 B & 
 TASK_PIDS="$TASK_PIDS $!" 
do_something 2 C &
 TASK_PIDS="$TASK_PIDS $!" 

status_printer & 
  PRINTER_PID=$! 

# Wait for background tasks 
wait $TASK_PIDS 

# Stop special printer task instead of doing just
#  kill $PRINTER_PID >/dev/null
touch "${STATUS_BASENAME}.done"
wait $PRINTER_PID

# Cleanup 
rm -f "${STATUS_BASENAME}."* 

Yes, it is very much possible.

Assuming that your existing code (based on your prior posts) currently goes something like this:

do_something() {
  ...
  echo -ne "\r$index of $No_of_Files Completed"
  ...
}

do_something A &
do_something B &
do_something C &
wait

...then you can perform the following adjustments in order to achieve the effect that you had in mind:

# Background tasks will no longer write directly to the console; instead, 
#  they will write to temporary files which will be read periodically 
#  by a special log printer task (which will display everything nicely.) 
# 
# Name of temporary files 
STATUS_BASENAME="/tmp/~$.status" 
# Process IDs of backgrounded tasks; we record them so we can wait on them 
#  specifically but not wait on the special log printer task 
TASK_PIDS="" 

do_something() { 
  # First parameter must be a task ID starting at 0 incremented by 1 
  TASK_ID=$1 ; shift 
  ...
  # We write new status to status file (note we don't echo -n, we want that 
  #  trailing newline) 
  # Try to go through a temporary status file which we rename afterwards to 
  #  avoid race conditions with the special log printer task 
  echo "$x of 5 Completed" >"${STATUS_BASENAME}.${TASK_ID}.tmp" 
  mv "${STATUS_BASENAME}.${TASK_ID}.tmp" "${STATUS_BASENAME}.${TASK_ID}" 
  ...
} 

# Special log printer task 
status_printer() { 
  # First time in the loop is special insofar we don't have to 
  #  scroll up to overwrite previous output. 
  FIRST_TIME=1 
  while true ; do 
    # If not first time, scroll up as many lines as we have 
    #  regular background tasks to overwrite previous output. 
    test $FIRST_TIME -eq 0 && for PID in $TASK_PIDS ; do 
      echo -ne '\033M' # scrol up one line using ANSI/VT100 cursor control sequences 
    done 
    FIRST_TIME=0
    TASK_ID=0
    for PID in $TASK_PIDS ; do 
      # If status file exists print first line 
      test -f "${STATUS_BASENAME}.${TASK_ID}" && head -1 "${STATUS_BASENAME}.${TASK_ID}" || echo "waiting..." 
      TASK_ID=`expr $TASK_ID + 1` # using expr for portability :) 
    done 
    test -f "${STATUS_BASENAME}.done" && return
    sleep 1 # seconds to wait between updates 
  done 
} 

do_something 0 A &
 TASK_PIDS="$TASK_PIDS $!" 
do_something 1 B & 
 TASK_PIDS="$TASK_PIDS $!" 
do_something 2 C &
 TASK_PIDS="$TASK_PIDS $!" 

status_printer & 
  PRINTER_PID=$! 

# Wait for background tasks 
wait $TASK_PIDS 

# Stop special printer task instead of doing just
#  kill $PRINTER_PID >/dev/null
touch "${STATUS_BASENAME}.done"
wait $PRINTER_PID

# Cleanup 
rm -f "${STATUS_BASENAME}."* 
自演自醉 2024-08-31 15:27:04

就像 pv -c 所做的那样?

当然,请参阅 src/pv/cursor.c 。虽然仅通过 shell 并不真正安全,但一个小型 C 实用程序可以处理它。

Like what pv -c does?

Sure, see src/pv/cursor.c. Although it's not really safely doable from shell only, a small C utility could take care of it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文