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


10

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

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

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

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


3
補足:ファイルが開かれると、プロセスはファイル記述子とiノードデータを使用してファイルを操作します。パスを変更する(つまり、ファイルを移動する)ことで、プロセスにあまり問題が発生することはありません。
ジョンWHスミス

2
外部プロセスを管理していますか?外部プロセスが一時ファイルを作成し、ファイルへの書き込みが完了したらファイルの名前を変更することは可能ですか?
ジェニーD

@JennyD私はいくつかの調査を行い、それは事実であることが判明しました。私は必要としないlsofだけで、ファイルの拡張子がないかどうかを確認するために必要なすべてのIで.tmp。それはささいなことです。しかし、私は私が少しのことを学んだので、私は私の質問をうれしいlsofinotifyとか。
Peter Kovac、2014

@PeterKovac私も答えを読んでそれらについてもっと知りましたので、あなたが質問してくれてとてもうれしいです。
ジェニーD

@JohnWHSmith-通常、同じファイルシステム内でファイルを移動する場合は、書き込みが完了する前に新しいファイルシステムにファイルを移動すると、データが失われます。
ジョニー14

回答:


11

lsofマニュアルページから

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


ありがとう、知らなかったinotify。残念ながら、私の箱にはインストールされていませんが、どこかでパッケージが見つかるはずです。ファイルを閉じる必要がある理由については、私の編集を参照してください。これはデータセットであり、さらに処理する前に完了する必要があります。
Peter Kovac、2014

1
もう1つの注意点:inotifywaitスクリプトが2回「ポーリング」するのを防ぎますが、OPは引き続きlsofループでチェックインする必要があります。ファイルを2回開いた場合、ファイルをinotify準備する準備ができていなくても、1回閉じるとイベントがトリガーされます。操作済み(たとえば、最後のコードスニペットでは、sleep呼び出しをに置き換えることができますinotifywait)。
ジョンWHスミス

@John a close_writeは、一度に1つのプロセスのみが書き込み用にファイルを開くことができるため、問題ありません。それはそれが閉じられた直後に別の人がそれを開かないことを前提としていますが、それから同じ問題がlsofポーリングで存在します。
Graeme

1
@GraemeこれはOPの場合の設計により当てはまる可能性がありますが、カーネルは書き込みのためにファイルを2回開くことを許可します(この場合、CLOSE_WRITE2回トリガーされます)。
ジョンWHスミス

@John、更新されました。
Graeme

4

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

process1 input_file.dat | process2 > output_file.dat

利点:

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

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

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

これにより、最初のプロセスがファイルを書き込むまでの距離に関係なく、入力ファイルの読み取りが最初から開始されます(まだ開始されていない、または既に完了している場合でも)。


ええ、それは「明白な」解決策でしょう。残念ながら、データ生成プロセスは私の制御の範囲外です(他のユーザーが実行しています)。
Peter Kovac、2014

@PeterKovacそれは無関係です:猫input_file.dat | process2 output_file.dat
MariusMatutiae 2014

@MariusMatutiaeが終了する前に終了する可能性がcatあります。彼らはブロックしません。process2process1
cpugeniusmv 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.