web-dev-qa-db-ja.com

ファイルを移動しますが、ファイルが閉じている場合のみ

外部プロセスで作成された大きなファイルを閉じたらすぐに移動したい。

このテストコマンドは正しいですか?

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

編集:ファイルはデータセットを表しており、別のプログラムが動作できるように、移動が完了するまで待つ必要があります。そのため、外部プロセスがファイルで行われたかどうかを知る必要があります。

10
Peter Kovac

lsof manページから

Lsofは、コマンド名、ファイル名、インターネットアドレスまたはファイル、ログイン名、NFSファイル、PID、PGID、またはリストに要求されたUIDの特定に失敗した場合など、エラーが検出された場合に1を返します。 -Vオプションが指定されている場合、lsofはリストに失敗した検索項目を示します。

したがって、これはlsof failed for some other reason句が実行されないことを示唆しています。

外部プロセスがまだ開いている間にファイルを移動しようとしたことがありますか?宛先ディレクトリが同じファイルシステム上にある場合、基礎となるiノードは同じままであるため、3番目のプロセスからの元のパスでアクセスする必要がない限り、それを行うことで問題はありません。それ以外の場合、私はmvがとにかく失敗すると思います。

外部プロセスがファイルで完了するまで本当に待機する必要がある場合は、繰り返しポーリングするのではなく、ブロックするコマンドを使用することをお勧めします。 Linuxでは、inotifywaitを使用できます。例えば:

 inotifywait -e close_write /path/to/file

lsofを使用する必要がある場合(おそらく移植性のため)、次のようなことを試すことができます。

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

更新

以下の @ JohnWHSmith で示されているように、複数のプロセスが書き込み用にファイルを開いている可能性があるため、最も安全な設計では常にlsofループを使用します(例本当に読み取り専用である必要があるときに、読み取り/書き込みフラグでファイルを開く、不十分に作成されたインデックス作成デーモンである可能性があります。ただし、スリープの代わりにinotifywaitを使用できます。スリープ行をinotifywait -e close /path/to/fileに置き換えてください。

11
Graeme

代替アプローチとして、これはパイプの完璧なケースです。2番目のプロセスは最初のプロセスからの出力を次のように処理します。利用可能になり次第、プロセス全体が完了するのを待つのではなく、

process1 input_file.dat | process2 > output_file.dat

利点:

  • 一般的にはるかに高速:
    • ディスクに読み書きする必要はありません(ramdiskを使用すれば、これを回避できます)。
    • マシンリソースをより完全に使用する必要があります。
  • 終了後に削除する中間ファイルはありません。
  • OPのように複雑なロックは必要ありません。

パイプを直接作成する方法はないがGNU coreutilsがある場合は、次のように使用できます。

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

これにより、入力ファイルの読み取りが最初から開始されます最初のプロセスがファイルの書き込みを介して(たとえまだ開始されていないか、すでに完了していても)。

4
l0b0