Loop While para testar se existe um arquivo no bash

93

Estou trabalhando em um script de shell que faz certas alterações em um arquivo txt apenas se ele existir, no entanto, esse loop de teste não funciona, eu me pergunto por quê? Obrigado!

while [ ! -f /tmp/list.txt ] ;
do
      sleep 2
done
Zenet
fonte
3
Não posso dizer que estou surpreso; esse loop não tenta mudar nada.
Ignacio Vazquez-Abrams
O ponto e vírgula é redundante. De que forma esse loop de teste não funciona? Ele ficará inativo iterativamente por 2 segundos até que o arquivo /tmp/list.txt exista.
Jonathan Leffler
5
Funciona para mim - o loop termina quando o arquivo é criado fora do script.
1
na verdade, esse loop só serve para esperar até que o arquivo esteja lá, o resto do meu script faz as alterações ...: p
Zenet
1
Então o loop while funciona, sou só eu ... desculpe.
Zenet

Respostas:

143

Quando você diz "não funciona", como sabe que não funciona?

Você pode tentar descobrir se o arquivo realmente existe adicionando:

while [ ! -f /tmp/list.txt ]
do
  sleep 2 # or less like 0.2
done
ls -l /tmp/list.txt

Você também pode se certificar de que está usando um shell Bash (ou relacionado) digitando 'echo $ SHELL'. Acho que CSH e TCSH usam uma semântica ligeiramente diferente para este loop.

CWF
fonte
Por que você está usando a verificação de arquivo invertido? O while [-f /tmp/list.txt] não deve ser usado?
dia
2
@valentt Nenhum loop de corte diz literalmente "enquanto o arquivo NÃO existir, durma" .. se você remover o 'NOT' o loop será interrompido instantaneamente
Kenyakorn Ketsombut
2
em 1 linha:while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
DrumM
55

Se você estiver no Linux e tiver o inotify-tools instalado, você pode fazer isso:

file=/tmp/list.txt
while [ ! -f "$file" ]
do
    inotifywait -qqt 2 -e create -e moved_to "$(dirname $file)"
done

Isso reduz o atraso introduzido pelo hibernar enquanto ainda pesquisa a cada "x" segundos. Você pode adicionar mais eventos se antecipar que eles são necessários.

yingted
fonte
4
+1 para eficiência. Juntar-se ao sono é feio. Para quem não sabe o inotifywait - está no pacote inotify-tools.
Michał Šrajer
7
Essa é uma ferramenta extremamente útil. Para qualquer um que esteja se perguntando por que o loop, é para lidar com as possíveis condições de corrida entre a criação e a espera e porque inotifywait tem --excludeque filtrar os nomes dos arquivos, mas não --includeignorar tudo, exceto o nome do arquivo. O comando acima deve usar o -qqargumento em vez de >&/dev/nullembora.
Craig Ringer
t é o --timeout, não a frequência de verificação, não? O ponto de inotifywait é que não há votação
Alex Dean
1
@AlexDean O tempo limite é para evitar uma condição de corrida. Polling com sleep é lento porque o loop não sairá durante o sleep, mas inotifywait sairá antes do tempo limite se ele vir um evento.
yingted
1
@AlexDean Sim, mas é necessário evitar uma condição de corrida TOCTTOU. Caso contrário, inotifywaitpode travar indefinidamente se um arquivo for criado antes de começar a escutar eventos.
yingted em
4

Eu tive o mesmo problema, coloque o! fora dos colchetes;

while ! [ -f /tmp/list.txt ];
do
    echo "#"
    sleep 1
done

Além disso, se você adicionar um eco dentro do loop, ele dirá se você está entrando no loop ou não.

David Cox
fonte
2

Eu me deparei com um problema semelhante e isso me trouxe até aqui, então eu só queria deixar a minha solução para qualquer pessoa que tenha a mesma experiência.

Descobri que, se executasse, cat /tmp/list.txto arquivo estaria vazio, embora eu tivesse certeza de que havia conteúdo sendo colocado imediatamente no arquivo. Acontece que se eu colocar um sleep 1;pouco antes de cat /tmp/list.txtele funcionou como esperado. Deve ter havido um atraso entre a hora em que o arquivo foi criado e a hora em que foi escrito, ou algo parecido.

Meu código final:

while [ ! -f /tmp/list.txt ];
do
    sleep 1;
done;
sleep 1;
cat /tmp/list.txt;

Espero que isso ajude alguém a economizar meia hora frustrante!

Zane Hooper
fonte
1

Como @ zane-hooper, tive um problema semelhante no NFS. Em sistemas de arquivos paralelos / distribuídos, o intervalo entre você criar um arquivo em uma máquina e a outra máquina "vê-lo" pode ser muito grande, então eu poderia esperar até um minuto inteiro após a criação do arquivo antes que o loop while saia (e também há um efeito colateral de "ver" um arquivo já excluído).

Isso cria a ilusão de que o script "não funciona" , embora, na verdade, seja o sistema de arquivos que está perdendo o controle.

Demorei um pouco para descobrir, espero que poupe algum tempo.

PS Isso também causa um número irritante de erros de "Manipulador de arquivo obsoleto".

Ruslan Shaydulin
fonte
0

funciona com bash e sh:

touch /tmp/testfile
sleep 10 && rm /tmp/testfile &
until ! [ -f /tmp/testfile ]
do
   echo "testfile still exist..."
   sleep 1
done
echo "now testfile is deleted.."
Neerajmalve
fonte
0

Esta é uma versão com um tempo limite para que, após um certo tempo, o loop termine com um erro:

# After 60 seconds the loop will exit
timeout=60

while [ ! -f /tmp/list.txt ];
do
  # When the timeout is equal to zero, show an error and leave the loop.
  if [ "$timeout" == 0 ]; then
    echo "ERROR: Timeout while waiting for the file /tmp/list.txt."
    exit 1
  fi

  sleep 1

  # Decrease the timeout of one
  ((timeout--))
done
ZedTuX
fonte
-3

faça isso deste modo

while true
do
  [ -f /tmp/list.txt ] && break
  sleep 2
done
ls -l /tmp/list.txt
ghostdog74
fonte