Detectar comandos de longa execução e notificar quando eles forem concluídos

5

Esta é uma chance remota - eu não acho que é possível, mas eu pensei em perguntar.

Freqüentemente executo comandos que demoram alguns minutos para serem concluídos e, enquanto eles são executados, verifico meu e-mail ou navego na Internet. Quando me lembro, corro command; finished , onde finished é um comando que abre uma caixa de diálogo, notificando que o comando está pronto. No entanto, nem sempre me lembro, por isso gostaria de ter algo que detecta automaticamente quando um comando demora mais de N segundos e executa finished quando o processo é concluído. Se isso importa, eu prefiro usar o zsh, mas eu mudaria se outro shell fosse capaz disso.

O iTerm (um emulador de terminal para o OSX) tem um recurso como este, onde ele mostra uma notificação quando o terminal fica ocioso ou produz uma saída após ficar ocioso por algum tempo. Se houver algum emulador de terminal para Linux que você saiba que tenha esse recurso, isso funcionaria tão bem (ou melhor).

Você consegue pensar em alguma maneira de fazer isso funcionar? Muito obrigado!

    
por Haldean Brown 11.07.2012 / 03:35

2 respostas

4

Você precisa de uma maneira de encontrar seus processos que estão sendo executados há algum tempo. Para isso, é útil saber que o parâmetro etime mostra o tempo decorrido desde o início do processo, no formato DD-hh: mm: ss, em que cada parte maior é opcional. Então você pode fazer

ps -U sauer -o etime,pid,command

(no AIX, você pode usar ps -U sauer -o "%t,%p,%c" )

Você poderia usar um -t $( tty) no lugar do -U username para selecionar processos no tty atual (também conhecido como no terminal atual). Isso é o que farei depois. Além disso, observe que fazer -o cmd= suprime o cabeçalho da coluna, mas significa que você precisa de várias opções -o.

Então, você acaba com ps -t $(tty) -o etime= -o pid= -o command= para mostrar todos os processos em execução no terminal atual, mostrando as colunas "tempo transcorrido do relógio", "ID do processo" e "comando" e suprimindo os cabeçalhos das colunas.

Agora você precisa obter apenas os que estão sendo executados por mais tempo do que os segundos. Então, você precisa extrair a primeira coluna e converter o tempo em segundos. Que tal um script de shell para fazer isso (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

Algumas coisas podem precisar ser explicadas. O ${part#0} está lá para que os tempos como "06" segundos sejam convertidos para "6" em vez de serem tratados como octal. Claro, octal 06 e decimal 6 são os mesmos, mas octal 09 não é válido, e shells às vezes reclamam disso.

A outra coisa é que ${var##*:} avalia para qualquer $ var com a maior cadeia correspondente a "*:" removida da frente (uma # é a correspondência mais curta e %% / % faz a mesmo do final). Se o padrão não corresponder, ele será avaliado como $var . Então, tiramos tudo menos os segundos, adicionamos isso a segundos e depois removemos os segundos do final. Se temos coisas sobrando, são minutos. Então, tire os minutos, converta em segundos, adicione ao total, limpe-o do final. E assim por diante.

Tudo bem. De lá, agora você só precisa colocar um #!/bin/ksh (ou bash, se for necessário) no topo, e você tem um script. Exceto que você também precisa de uma maneira de encontrar apenas os pids que estão sendo executados por mais de X segundos. Que tal um arquivo em / tmp. Vamos usar isso no lugar do comando 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

Agora tudo o que você precisa fazer é envolver toda a coisa gigante dentro de um loop while

enquanto verdadeiro   Faz     # o loop while grande e a instrução if     dormir 10   feito

Chame process_monitor.sh e coloque um process_monitor.sh& no seu .profile ou .bash_login ou o que for. Ele ficará em segundo plano assistindo a todos os processos naquele terminal e, quando encontrar um que esteja em execução por mais de 120 segundos, ele ficará na lista para assisti-lo e enviará um e-mail quando o processo terminar.

Há coisas que eu provavelmente adicionaria para torná-lo mais bonito, como capturar o sinal EXIT e remover os arquivos temporários quando o script sair, etc. Mas isso é um exercício para você. Espero que isso seja o suficiente para você ficar bem no caminho. :)

    
por 11.07.2012 / 05:37
2

Eu acabei de experimentar um pequeno aplicativo que faz exatamente o que você deseja: link disponível no mundo Debian / Ubuntu como um pacote. Não funcionou para mim até que reiniciei o Konsole, que é o meu aplicativo de terminal com guias.

    
por 15.02.2013 / 01:58

Tags