Como posso esperar por um arquivo no script de shell?

10

Estou tentando escrever um script de shell que esperará que um arquivo apareça no diretório /tmp chamado sleep.txt e, assim que ele for encontrado, o programa será encerrado, caso contrário, quero que o programa fique em suspensão. (suspenso) até que o arquivo seja localizado. Agora, estou assumindo que usarei um comando de teste. Então, algo como

(if [ -f "/tmp/sleep.txt" ]; 
then stop 
   else sleep.)

Eu sou novo em escrever um script de shell e qualquer ajuda é muito apreciada!

    
por Mo Gainz 17.02.2015 / 13:23

5 respostas

15

No Linux, você pode usar o subsistema de kernel inotify para aguardar com eficiência a aparência de um arquivo em um diretório:

while read i; do if [ "$i" = sleep.txt ]; then break; fi; done \
   < <(inotifywait  -e create,open --format '%f' --quiet /tmp --monitor)
# script execution continues ...

(supondo Bash para a sintaxe de redirecionamento de saída <() )

A vantagem desta abordagem em comparação com o polling de intervalo de tempo fixo como em

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# script execution continues ...

é que o kernel dorme mais. Com uma especificação de evento inotify como create,open , o script é apenas planejado para execução quando um arquivo em /tmp é criado ou aberto. Com o polling de intervalo de tempo fixo, você desperdiça ciclos de CPU para cada incremento de tempo.

Eu incluí o evento open para também registrar touch /tmp/sleep.txt quando o arquivo já existe.

    
por 17.02.2015 / 23:30
6

Basta colocar seu teste no loop while :

while [ ! -f /tmp/sleep.txt ]; do sleep 1; done
# next command
    
por 17.02.2015 / 13:30
1

A resposta aceita realmente funciona (obrigado maxschlepzig), mas deixa o inotifywait monitorando em segundo plano até o seu script sair. A única resposta que corresponde a exatamente seus requisitos (por exemplo, esperar que sleep.txt apareça dentro de / tmp) parece ser de Stephane, se o diretório a ser monitorado por inotifywait for alterado de ponto (.) Para '/ tmp'.

No entanto, se você estiver disposto a usar um diretório temporário SOMENTE para colocar seu sinalizador sleep.txt e pode apostar que ninguém mais colocará nenhum arquivo nesse diretório, apenas solicite inotifywait para assistir a este diretório para criações de arquivos seria suficiente:

1º passo: crie o diretório que você irá monitorar:

directoryToPutSleepFile=$(mktemp -d)

2º passo: verifique se o diretório está realmente lá

until [ -d $directoryToPutSleepFile ]; do sleep 0.1; done

3º passo: aguarde até que QUALQUER ficheiro apareça dentro de $directoryToPutSleepFile

inotifywait -e create --format '%f' --quiet $directoryToPutSleepFile

O arquivo que você colocará em $directoryToPutSleepFile pode ser chamado sleep.txt awake.txt, o que for. No momento em que o arquivo any é criado dentro de $directoryToPutSleepFile , seu script continuará após a instrução inotifywait .

    
por 27.11.2017 / 15:28
1

Existem alguns problemas com algumas das abordagens baseadas em inotifywait dadas até agora:

  • eles não conseguem encontrar um arquivo sleep.txt que tenha sido criado primeiro como um nome temporário e depois renomeado para sleep.txt . É preciso corresponder a moved_to eventos além de create
  • os nomes de arquivos podem conter caracteres de nova linha, a impressão dos nomes dos arquivos delimitados por nova linha não é suficiente para determinar se um sleep.txt foi criado. E se um arquivo foo\nsleep.txt\nbar fosse criado, por exemplo?
  • e se o arquivo for criado antes de inotifywait ter sido iniciado e instalado o relógio? Então inotifywait esperaria para sempre por um arquivo que já está aqui. Você precisa ter certeza de que o arquivo ainda não está após o relógio ter sido instalado.
  • algumas das soluções deixam inotifywait em execução (pelo menos até que outro arquivo seja criado) após a localização do arquivo.

Para resolver isso, você poderia:

sh -c 'echo "$$" &&
        LC_ALL=C exec inotifywait -me create,moved_to --format=/%f/ . 2>&1' | {
  IFS= read pid &&
    while IFS= read -r line && [ "$line" != "Watches established." ]; do
      : wait for watches to be established
    done
  [ -e sleep.txt ] || [ -L sleep.txt ] || grep -qxF /sleep.txt/ && kill "$pid"
}

Observe que estamos aguardando a criação de sleep.txt no diretório atual, . (para que você faça um cd /tmp || exit em seu exemplo). O diretório atual nunca muda, portanto, quando essa linha de tubulação retorna com sucesso, é um sleep.txt no diretório atual que foi criado.

É claro que você pode substituir . por /tmp acima, mas enquanto inotifywait estiver em execução, /tmp poderia ter sido renomeado várias vezes (não é provável que seja /tmp , mas algo a considerar no caso geral ) ou um novo sistema de arquivos montado nele, assim, quando o pipeline retorna, pode não ser um /tmp/sleep.txt que tenha sido criado, mas /new-name-for-the-original-tmp/sleep.txt . Um novo diretório /tmp também poderia ter sido criado no intervalo e esse não seria visto, então um sleep.txt criado lá não seria detectado.

    
por 27.11.2017 / 16:01
0

em geral, é muito difícil fazer qualquer coisa, a não ser o simples loop de teste / suspensão confiável. O principal problema é que o teste será executado com a criação do arquivo - a menos que o teste seja repetido, o evento de criação pode ser perdido. Esta é, de longe, a sua melhor aposta na maioria dos casos em que você estaria usando o Shell para começar:

#!/bin/bash
while [[ ! -e /tmp/file ]] ; do
    sleep 1
done

Se você achar que isso é insuficiente devido a problemas de desempenho, usar o Shell provavelmente não é uma ótima ideia.

    
por 04.07.2018 / 19:34