2013-03-07 101 views
1

我在bash脚本中显示命令进度。命令输出被传送到zenity --progress并且最终可以运行很长时间。我想放弃它(杀死命令),如果我取消zenity对话框:在使用zenity的bash脚本中 - 进度条从管道读取,如何安全地杀死管道进程?

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

所有的解决方案,我发现之一:

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

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

解决方案2.我想要做什么,但在一个过于复杂的方式(例如参照的例子here(法语))。如果可能,我想避免fifos或临时文件。看起来好像它最多可以通过输出重定向到一个文件描述符来完成,但我无法确切知道如何。

注:用于与重定向例如全组命令替换,$((...命令&回波> & 3 ... | zenity --progress)3> & 1) - 这是一个通用soution在这种类型的情况 - 在这里不起作用,因为$(...)一直等到整个子shell完成。

+0

'echo 0'只在我们运行'zenity --progress --pulsate'的情况下才需要;当'命令'不输出数字时,通常这样看起来更好。 – oriol 2013-03-07 08:56:15

回答

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) 
0

这应该工作:

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

非常好,紧凑的解决方案。只有: 1)从第二行必须在协同过程结束之前运行的意义上讲,它很脆弱。行之间的小小等待会破坏<$ {COPROC [0]}部分。 2)它不是POSIX。虽然bash可以,但我想知道是否有POSIX兼容解决方案。 – oriol 2013-03-07 14:40:59

+0

coproc的另一个问题是<&$ {COPROC [0]} [不能被重定向到子shell](http://stackoverflow.com/questions/10867153)。如果zenity成功,我想运行一些命令,如果失败,我想运行一些命令,但是我必须在不产生子shell的情况下执行此操作。 – oriol 2013-03-07 17:14:06

+0

重定向到子shell问题是相当简单的克服:exec 3 <&$ {COPROC [0]}然后(subshel​​l)<&3 – oriol 2013-03-07 18:34:54

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便携,它的是强劲罕见的情况下,第二个'回声'超过第一个(例如,由于bug而子午线失败并且子进程恰好在后面启动)