Move o arquivo, mas somente se estiver fechado

10

Eu quero mover um arquivo grande criado por um processo externo assim que ele é fechado.

Este comando de teste está correto?

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

EDIT: o arquivo representa um conjunto de dados e eu tenho que esperar até que esteja completo para movê-lo para que outro programa possa agir sobre ele. É por isso que preciso saber se o processo externo é feito com o arquivo.

    
por Peter Kovac 29.12.2014 / 10:18

2 respostas

11

Da página lsof man

Lsof returns a one (1) if any error was detected, including the failure to locate command names, file names, Internet addresses or files, login names, NFS files, PIDs, PGIDs, or UIDs it was asked to list. If the -V option is specified, lsof will indicate the search items it failed to list.

Isso sugere que sua cláusula lsof failed for some other reason nunca será executada.

Já tentou mover o arquivo enquanto o processo externo ainda o mantém aberto? Se o diretório de destino estiver no mesmo sistema de arquivos, não deverá haver problemas em fazer isso, a menos que você precise acessá-lo sob o caminho original de um terceiro processo, pois o inode subjacente permanecerá o mesmo. Caso contrário, acho que mv falhará de qualquer maneira.

Se você realmente precisar esperar até que o processo externo termine com o arquivo, é melhor usar um comando que bloqueia em vez de pesquisar repetidamente. No Linux, você pode usar inotifywait para isso. Por exemplo:

 inotifywait -e close_write /path/to/file

Se você precisar usar lsof (talvez para portabilidade), pode tentar algo como:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

Atualizar

Conforme observado por @ JohnWHSmith abaixo, o design mais seguro sempre usaria um loop lsof como acima, pois é possível que mais de um processo tenha o arquivo aberto para gravação (um exemplo pode ser um daemon de indexação mal escrito que abre arquivos com o sinalizador de leitura / gravação quando realmente deveria ser lido somente). inotifywait ainda pode ser usado em vez de dormir, basta substituir a linha de suspensão por inotifywait -e close /path/to/file .

    
por 29.12.2014 / 10:50
4

Como uma abordagem alternativa, esse é o caso perfeito para um canal - o segundo processo processará a saída do primeiro processo assim que estiver disponível, em vez de esperar que o processo completo seja concluído:

process1 input_file.dat | process2 > output_file.dat

Vantagens:

  • Muito mais rápido em geral:
    • Não precisa gravar e ler do disco (isso pode ser evitado se você usar um disco virtual).
    • Deveria usar os recursos da máquina mais completamente.
  • Nenhum arquivo intermediário a ser removido após o término.
  • Nenhum bloqueio complexo necessário, como no OP.

Se você não tem como criar diretamente um pipe, mas você tem GNU coreutils , você pode usar isto:

tail -F -n +0 input_file.dat | process2 > output_file.dat

Isto irá começar a ler o arquivo de entrada desde o início, não importa quão longe o primeiro processo esteja escrevendo o arquivo (mesmo que ainda não esteja iniciado ou já esteja pronto).

    
por 29.12.2014 / 11:31