在一个使用zenity的bash脚本 - 从管道读取进度条,如何安全地杀死管道进程?

时间:2013-03-07 08:25:29

标签: bash progress fifo zenity

我在bash脚本中显示命令进度。命令输出通过管道输送到zenity --progress,最终可以运行很长时间。如果我取消zenity对话框,我想中止它(杀死该命令):

( echo 0; command; echo 100 ) | if ! zenity --progress
                                then DO_SOMETHING_TO_KILL_command
                                fi

我找到的所有解决方案之一:

  1. 一般用pgrep,pidof,pkill,killall等杀死“命令”。这不是我想要的,因为可能有很多这样的“命令”在运行。

  2. 创建一个fifo来输出“command”的PID(命令& echo $!> some_fifo; wait),然后在管道后读取它。

  3. 解决方案2.做我想要的,但是过于复杂的方式(参见例如here(法语))。如果可能的话,我想避免使用fifos或temp文件。似乎最多可以通过输出重定向到文件描述符来完成,但我无法确切地知道如何。

    注意:使用重定向命令替换整个组,例如$((... command& echo>& 3 ... | zenity --progress)3>& 1) - 这是一个通用的在这种情况下的情况 - 在这里不起作用,因为$(...)等待整个子shell完成。

3 个答案:

答案 0 :(得分:1)

最强大的实施之一:

#!/bin/bash

( # the pipe creates an implicit subshell; marking it explicit
 (
  sleep 10 & echo $! >&3
  wait
  echo 100 # to stdout, first pipe
 ) | (
  if ! zenity --progress
  then echo zen_progress_aborted >&3
  fi
 )
) 3>&1 | (
      read FD3_FIRST
      read FD3_SECOND
      # the PID comes first and the zenity output second almost always
      #+but we cannot be sure so:
      if [ "$FD3_SECOND" = "zen_progress_aborted" ]
       then kill $FD3_FIRST
      elif [ "$FD3_FIRST" = "zen_progress_aborted" ]
       then kill $FD3_SECOND
      fi
     )
# 'echo 100' after 'wait' means zenity does not exit after the command ends
#+so no need to kill it in this case

exit 0

它比使用&coproc'复杂得多。但它是POSIX便携式的,它在极少数情况下很强大,其中第二个回声'超过第一个(例如,由于错误而导致zenity失败,并且子流程恰好在稍后启动)

答案 1 :(得分:1)

一个简单的POSIX版本:

( # the pipe creates an implicit subshell; marking it explicit
 ( sleep 10; echo 100 )& echo $!
) | (
 read PIPED_PID; zenity --progress || kill $PIPED_PID
)
如果zenity失败,

也有效。即使删除了第一个command(在这种情况下为sleep 10),echo $!也会始终首先输出到管道,因此我们会在运行进度条之前读取它。

当长commandsleep 10以上)未输出进度数时,这是一个更简单的变体:

sleep 10 & PIPED_PID=$!
tail -f /dev/null --pid $PIPED_PID | ( zenity --progress || kill $PIPED_PID )

wait在这里不起作用,因为管道创建了一个子shell,它是&子进程的兄弟。这适用于除bash之外的shell,但tail --pid不是POSIX标准。 POSIX版本:

sleep 10 & PIPED_PID=$!
while kill -s 0 $PIPED_PID; do sleep .1; done | ( zenity --progress || kill $PIPED_PID )

答案 2 :(得分:0)

这应该有效:

coproc { echo 0; command; echo 100; }
zenity --progress <&${COPROC[0]} || kill $!