Como posso terminar um processo se ocorrer um segundo evento inotify?

2
inotifywait -q -m -e close_write,create --recursive ../orgmode-parse-print | 
while read -r filename event; do
    echo $filename;
    echo $event
    sleep infinity;
done

O problema acima é que ele 'dorme' para sempre e nunca termina. Como posso terminar ou reiniciar o processo (essencialmente o conteúdo do loop while (incluindo o sleep )) se ocorrer outro evento?

Em outras palavras, faça o comando, mas termine-o (interrompa-o, suponho) e comece novamente se um arquivo tiver sido modificado.

Estou usando sleep como exemplo aqui - o processo real que está sendo executado é um processo de longa execução.

    
por Chris Stryczynski 15.07.2018 / 17:34

1 resposta

1

Isso funciona para mim:

$ inotifywait -q -m -e close_write,create --recursive dir1/ | \
  ( 
    CNT=0; 
    while read -r filename event; do 
       echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); 
       [ "$CNT" -eq 1 ] && exit; 
    done 
  )

Exemplo

Para começar, criei uma estrutura de diretório de exemplo para trabalhar com:

$ mkdir -p dir1/dir2/dir{3..5}

$ tree dir1/
dir1/
└── dir2
    ├── afile
    ├── dir3
    ├── dir4
    └── dir5

4 directories, 1 file

Eu então executei isso para começar a ver o diretório:

$ inotifywait -q -m -e close_write,create --recursive dir1/ | ( CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); [ "$CNT" -eq 1 ] && exit; done )

Eu, então, executei os comandos touch afile neste diretório:

$ cd dir1/dir2
$ touch afile
$ touch afile

Isso resultou nessa saída do inotifywait :

count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile

Quando chega ao 2º "evento", sai.

Problemas e uma solução melhor

Um problema com o uso do subshell (...while ...) no pipe é que não vemos a segunda mensagem de echo quando o segundo evento ocorre. Não há problema, podemos simplesmente reestruturar coisas como esta:

$ CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); [ "$CNT" -eq 2 ] && break; done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
$

Expandido:

$ CNT=0; \
  while read -r filename event; do \
    echo "count: $CNT filename: $filename  event: $event"; ((CNT++)); \
    [ "$CNT" -eq 2 ] && break; \
  done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
$

Com uma tarefa em segundo plano

Se você tiver uma tarefa que irá bloquear dentro do loop while ... , você pode introduzir um trap para eliminá-la e, em segundo plano, permitir que o loop while ... processe a entrada de inotifywait . / p>

Exemplo:

$ cat ./notifier.bash
#!/bin/bash

trap 'kill $(jobs -p)' EXIT;

CNT=0
while read -r filename event; do
  sleep 1000 &
  echo "count: $CNT filename: $filename  event: $event"; ((CNT++))
  [ "$CNT" -eq 2 ] && break
done < <(inotifywait -q -m -e close_write,create --recursive dir1/)

Em ação:

$ ./notifier.bash
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
./notifier.bash: line 1: kill: (30301) - No such process

E não há vestígios do sleep procs em segundo plano:

$ ps -eaf|grep [s]leep
$

Após o último ajuste em relação ao trap que você pode ter percebido. Quando fazemos isso kill $(jobs -p) ele joga lixo na tela assim, às vezes:

./notifier.bash: line 1: kill: (30301) - No such process

Podemos limpar isso assim:

 trap 'kill $(jobs -p) > /dev/null 2>&1' EXIT;

Agora, quando executamos:

$ ./notifier.bash
count: 0 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/  event: CLOSE_WRITE,CLOSE afile
$

Referências

por 15.07.2018 / 19:05

Tags