检测长时间运行的命令并在其完成时通知

检测长时间运行的命令并在其完成时通知

这是一个不太可能的事情——我认为这是不可能的,但我想我会问一下。

我经常运行需要几分钟才能完成的命令,在运行这些命令时,我会查看电子邮件或浏览互联网。当我记起来时,我会运行command; finished,其中finished是一个命令,它会弹出一个对话框通知我命令已完成。但是,我并不总是记得,所以我希望有一些东西可以自动检测命令何时需要超过N几秒钟,并在过程完成时运行finished。如果这很重要,我更喜欢使用 zsh,但如果另一个 shell 能够做到这一点,我会切换。

iTerm(适用于 OSX 的终端仿真器)具有类似的功能,当终端空闲或空闲一段时间后产生输出时,它会向您显示通知。如果您知道任何适用于 Linux 的终端仿真器具有此功能,那么它的效果会一样好(甚至更好)。

你能想到什么办法来实现这个功能吗?非常感谢!

答案1

您需要一种方法来查找已运行一段时间的进程。为此,了解 etime 参数会显示自进程启动以来经过的时间,格式为 DD-hh:mm:ss,其中每个较大的部分都是可选的。因此,您可以这样做

ps -U sauer -o etime,pid,command

(在 AIX 上,您可以使用ps -U sauer -o "%t,%p,%c"

您可以使用 a-t $( tty)代替 来-U username选择当前 tty(即当前终端)上的进程。这就是我稍后要做的。另外,请注意,这样做-o cmd=会隐藏列标题,但这意味着您需要多个 -o 选项。

因此,您最终会ps -t $(tty) -o etime= -o pid= -o command=显示当前终端上运行的所有进程,显示“已用挂钟时间”、“进程 ID”和“命令”列,并抑制列标题。

现在您需要获取运行时间超过秒数的程序。因此,您需要提取第一列并将时间转换为秒数。使用 shell 脚本 (ksh/bash) 执行此操作如何:

ps -t $(tty) -o etime= -o pid= -o command= | while read time pid cmd
do
  secs=0
  while true
  do
    part=${time##*:}
    secs=$(( secs + ${part#0} ))
    [[ "$part" != "$time" ]] || break # only had seconds

    time=${time%:$part}
    part=${time##*:}
    secs=$(( secs + (60 * ${part#0}) ))
    [[ "$part" != "$time" ]] || break # only had minutes left

    time=${time%:$part}
    part=${time##*-}
    secs=$(( secs + (60 * 60 * ${part#0}) ))
    [[ "$part" != "$time" ]] || break # only had hours left

    time=${time%-$part} # all that's left are days-hours
    secs=$(( secs + (24 * 60 * 60 * time) ))
    break
  done

  echo "Process '$cmd' (pid '$pid') has been up for '$secs' seconds"
done

有几件事可能需要解释一下。之所以${part#0}要加上,是因为像“06”秒这样的时间会被转换为“6”,而不是被当作八进制处理。当然,八进制 06 和十进制 6 是一样的,但八进制 09 是无效的,而且 shell 有时会抱怨这一点。

另一件事是,它${var##*:}会计算出 $var 的值,其中匹配“*:”的最大字符串从前面删去(一个#是最短的匹配,而%%/%从末尾执行相同的操作)。如果模式不匹配,它会计算出$var。因此,我们删除除秒之外的所有内容,将其添加到秒中,然后从末尾删除秒。如果还有剩余,那就是分钟。因此,删除分钟,转换为秒,添加到累计总数中,从末尾删去。依此类推。

好的。现在您只需要#!/bin/ksh在顶部放置一个(或 bash,如果需要),您就会有一个脚本。但您还需要一种方法来查找运行时间超过 X 秒的 pid。/tmp 中的文件怎么样?让我们用它来代替 echo 命令:

  if [[ $secs -gt 120 ]]
  then
    { cat /tmp/pids.$$; echo "$pid $cmd"; } 2>/dev/null | sort -u > /tmp/pids.$$.tmp
    while read p c
    do
      if ps -p $p >/dev/null 2>&1
      then
        # put it back in the list
        echo "$p $c" > /tmp/pids.$$
      else
        echo "Command '$c' exited" | mailx -s "exit report" $USER
      fi
    done < /tmp/pids.$$.tmp
  fi

现在你要做的就是把整个巨大的东西包裹在一个while循环中

while true do # 大 while 循环和 if 语句 sleep 10 done

调用它process_monitor.sh并将 a 粘贴process_monitor.sh&到您的 .profile 或 .bash_login 或任何其他文件中。它将在后台监视该终端中的所有进程,当它发现一个进程运行时间超过 120 秒时,它会将其粘贴到要监视的列表中,并在进程退出时向您发送电子邮件。

我可能会添加一些东西来让它更漂亮,比如捕获 EXIT 信号并在脚本退出时删除临时文件等。但这对你来说是一项练习。我希望这足以让你顺利完成。:)

答案2

我刚刚尝试了一个小应用程序,它可以完全满足您的要求: https://github.com/jml/undistract-me 在 Debian/Ubuntu 世界中以软件包形式提供。直到我重新启动 Konsole(我首选的选项卡式终端应用程序)后,它才对我起作用。

相关内容