Determinar se o arquivo está sendo gravado?

25

Preciso implantar um processo automatizado (via script cron de 1 min) que procure por arquivos tar em um diretório específico. Se um arquivo tar for encontrado, ele não será desviado para o local apropriado e o arquivo tar será excluído.

Os arquivos tar são copiados automaticamente para este servidor pelo SSH de outro servidor. Em alguns casos, os arquivos tar são extremamente grandes, com muitos arquivos.

O problema que eu espero encontrar: Se demorar> 1 minuto para o arquivo tar ser copiado no servidor, e o script cron for executado uma vez a cada minuto, ele verá o arquivo .tar.gz e tentará descompacte-o, mesmo que o arquivo tar ainda esteja no processo de gravação.

Existe alguma maneira (via comandos bash) de testar se um arquivo está sendo gravado atualmente ou se é apenas um arquivo parcial, etc?

Uma alternativa em que eu pensava era fazer com que o arquivo fosse copiado como uma extensão de arquivo diferente (como .tar.gz.part) e renomeado para .tar.gzapós a conclusão da transferência. Mas achei que tentaria descobrir se existe simplesmente uma maneira de determinar se o arquivo está inteiro na linha de comando primeiro ... Alguma pista?

Jake Wilson
fonte
2
Como exatamente o arquivo está sendo transferido? Por exemplo, rsyncusa um nome de arquivo temporário durante a transferência (por padrão) e somente após a transferência completa do arquivo, renomeia-o para o nome do arquivo real.
Piskvor 27/03

Respostas:

12

Você está no caminho certo; renomear o arquivo é uma operação atômica; portanto, executar a renomeação após o upload é simples, elegante e não propenso a erros. Outra abordagem que posso pensar é usar lsof | grep filename.tar.gzpara verificar se o arquivo está sendo acessado por outro processo.

Alex
fonte
7
( lsof filename.tar.gzÉ mais eficiente e mais preciso do que lsof | grep filename.tar.gz)
Rico
BTW, deve ser um caminho absoluto do nome do arquivo
DennisLi
14

Sua melhor aposta é usar lsofpara determinar se um arquivo foi aberto por qualquer processo:

#  lsof -f -- /var/log/syslog
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
rsyslogd 1520 syslog    1w   REG  252,2    72692 16719 /var/log/syslog

Você não pode dizer com facilidade se está sendo gravado, mas se está sendo gravado, DEVE estar aberto.


Edit: vamos resolver o problema real aqui, em vez de tentar implementar a solução proposta!

Use rsync para transferir o arquivo:

  rsync -e ssh remote:big.tar.gz .

Dessa forma, o arquivo não será copiado por cima do existente, mas copiado para um arquivo temporário ( .big.tar.gz.XXXXXX) até a transferência ser concluída e, em seguida, movido para o local.

MikeyB
fonte
6

Um pouco velho, mas a maioria das respostas esquece completamente o objetivo da pergunta:

Mas achei que tentaria descobrir se existe simplesmente uma maneira de determinar se o arquivo está inteiro na linha de comando primeiro ...

Em geral, não há. Você simplesmente não tem informações suficientes para determinar isso.

Como determinar se o arquivo está fechado não é o mesmo que determinar se o arquivo está inteiro . Por exemplo, um arquivo será "fechado" se a conexão for perdida parcialmente durante a transferência.

Somente a resposta de @ Alex acertou. E até ele se apaixonou por usar lsofum pouco.

Para determinar se o arquivo foi totalmente transferido com sucesso, são necessários mais dados. Tal como:

Uma alternativa em que eu pensava era fazer com que o arquivo fosse copiado como uma extensão de arquivo diferente (como .tar.gz.part) e renomeado para .tar.gzapós a conclusão da transferência.

Essa é uma maneira perfeitamente correta de comunicar que o arquivo foi transferido total e com êxito. Você também pode mover arquivos de um diretório para outro, desde que permaneça no mesmo sistema de arquivos. Ou peça ao remetente que envie um filename.donearquivo vazio para sinalizar a conclusão.

Mas todos os métodos precisam contar com o remetente de alguma forma sinalizando que a transferência foi concluída com êxito. Porque apenas o remetente tem essa informação.

Alguns formatos de arquivo (como PDFs) possuem dados que permitem determinar se o arquivo está completo. Mas você precisa abrir e ler praticamente todo o arquivo para descobrir.

lsofapenas informará que o arquivo não está mais aberto - não informará por que não está mais aberto. Nem lhe dirá qual o tamanho do arquivo.

Andrew Henle
fonte
1
Não posso aprovar isso o suficiente. Bom trabalho resolvendo o problema XY aqui.
Beefster
5

A melhor maneira de fazer isso é usar o incron ("inotify cron system"). Ele permite que você defina uma inspeção inotify em um diretório que o notificará sobre as operações do arquivo. Nesse caso, você deve assistir o dir por um close_write. Isso permitirá que você execute seu comando assim que o arquivo for fechado após uma gravação.

Kyle
fonte
2

Parece que o lsof pode detectar em que modo um arquivo está aberto em:

lsof -f -- a_file
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
cat     52391 bob    1w   REG    1,2       15 19545007 a_file

Veja onde diz 1w? Isso significa que o número do descritor de arquivo é 1 e o modo é w ou write.

Kevin Baragona
fonte
O FDcampo mostra 3rpara mim quando o arquivo está aberto para leitura.
Sopalajo de Arrierez
0

O uso inotifywaitpode alcançar o que você procura - ele tem a capacidade de aguardar até que a gravação de um arquivo seja concluída antes de executar um comando.

A seguir, você assiste continuamente uma pasta a novos arquivos e executa o comando no loop ao concluir a gravação no arquivo.

WATCH_DIR=/directory/to/monitor
DEST_DIR=/x/y/z

/usr/bin/inotifywait --recursive --monitor --quiet -e moved_to -e close_write --format '%w%f' "$WATCH_DIR" | while read -r INPUT_FILE; do

mv "$0" "$DEST_DIR"

done

Para mais opções de configuração, consulte https://linux.die.net/man/1/inotifywatch

teeedubb
fonte