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!
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.2done
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.
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:
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.
+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 1done
Além disso, se você adicionar um eco dentro do loop, ele dirá se você está entrando no loop ou não.
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.
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".
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=60while[!-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 1fi
sleep 1# Decrease the timeout of one((timeout--))done
Respostas:
Quando você diz "não funciona", como sabe que não funciona?
Você pode tentar descobrir se o arquivo realmente existe adicionando:
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.
fonte
while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
Se você estiver no Linux e tiver o inotify-tools instalado, você pode fazer isso:
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.
fonte
--exclude
que filtrar os nomes dos arquivos, mas não--include
ignorar tudo, exceto o nome do arquivo. O comando acima deve usar o-qq
argumento em vez de>&/dev/null
embora.--timeout
, não a frequência de verificação, não? O ponto de inotifywait é que não há votaçãoinotifywait
pode travar indefinidamente se um arquivo for criado antes de começar a escutar eventos.Eu tive o mesmo problema, coloque o! fora dos colchetes;
Além disso, se você adicionar um eco dentro do loop, ele dirá se você está entrando no loop ou não.
fonte
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.txt
o arquivo estaria vazio, embora eu tivesse certeza de que havia conteúdo sendo colocado imediatamente no arquivo. Acontece que se eu colocar umsleep 1;
pouco antes decat /tmp/list.txt
ele 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:
Espero que isso ajude alguém a economizar meia hora frustrante!
fonte
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".
fonte
funciona com bash e sh:
fonte
Esta é uma versão com um tempo limite para que, após um certo tempo, o loop termine com um erro:
fonte
faça isso deste modo
fonte