web-dev-qa-db-ja.com

上書きせずに移動またはコピーし、成功を確認する

問題:宛先ファイルが存在する場合、それを上書きせずにファイルの名前を変更またはコピーし、移動またはコピー操作の成功を確認する方法を探しています。 MacOS/Unixにインストールされているmv/cpのBSDバージョン、およびLinuxにあるGNU coreutilsバージョン)で動作する方法を探しています。

解決策: mv/cpのすべてのバージョンで、-nフラグを使用して宛先ファイルを上書きしないようにすることができます。

mv -n file1 file2
cp -n file1 file2 

同様の質問は、終了ステータスを使用してmvとcpの成功をテストすることを提案します。終了ステータスは、成功した場合は0、エラーが発生した場合は> 0です。ただし、mv/cpの両方のバージョンで、宛先ファイルがすでに存在し、-nフラグが使用されている場合、終了コードは0です。

私が考えることができる他の唯一のオプションは、-vフラグも使用して、コマンドの出力を確認することです。

mv -nv file1 file2
cp -nv file1 file2

ただし、GNUとmv/cpのBSDバージョンは、-nvフラグが使用されており、file2がすでに存在する場合、動作が異なります。GNU mv/cpは何も返しませんが、BSDバージョンはfile2 not overwrittenを返します。

以前の方法では、宛先ファイルが最初に存在するかどうかを確認してから、mv/cp操作を実行していました。信じられないかもしれませんが、チェックが実行されてからmv/cp操作が実行されるまでの間に、宛先ファイルが別のプロセスによって作成されることがあったため、問題が発生しました。

BSDとGNU mv/cpのバージョンの両方で機能するこのタスクを実行する方法はありますか?

または、Python 2を使用してこれを行う方法はありますか?os.rename()を使用してこれを行う方法が見つかりませんでした

2
srcerer

bashがある場合- https://stackoverflow.com/questions/13828544/atomic-create-file-if-not-exists-from-bash-script

_set -o noclobber
{ > file ; } &> /dev/null
_

Fileという名前のファイルが存在しない場合、このコマンドはfileという名前のファイルを作成します。 fileという名前のファイルがある場合は、何もしません(ただし、ゼロ以外の戻りコードを返します)。

つまりこの手法を使用して、最初に空のファイルを作成します。それが成功した場合は、空のファイルを上書きできます。

同様にpythonの場合。 os.open()を使用して空のファイルを作成し、フラグに_O_EXCL_を必ず含めてください。 (「フラグとモード値の説明については、Cランタイムのドキュメントを参照してください。」 POSIX標準 / Linuxのマニュアルページ を参照してください)。


Bashテクニックは、舞台裏で_O_EXCL_を使用しています。 _RENAME_NOREPLACE_ もありますが、これはLinuxでの比較的最近の追加であり、OS Xには存在しないと思います。

4
sourcejedi

ファイルが同じファイルシステムにある場合は、 hardlink を作成できます。

ln SRC DEST

それが成功した場合は、ソースファイルを削除できます。

rm SRC
1
Roger Pate

FreeBSDは実際にcp -nはファイルの上書きを求められます:

$ rm -f foo.*
$ date > foo.1
$ date > foo.2
$ # this should fail
$ cp -n foo.1 foo.2 || echo fail
fail
$ rm foo.2
$ # this should succeed
$ cp -n foo.1 foo.2 || echo fail
$ exit

FreeBSDのmvは、宛先が存在する場合でも成功を返すと述べれば、あなたは正しいです。

$ rm -f foo.*
$ date > foo.1
$ date > foo.2
$ # this should fail
$ mv -n foo.1 foo.2 || echo fail
$ exit

回避策の1つは、&&mv結果コード[ ! -f src-file ]、次のように:

$ rm -f foo.*
$ date > foo.1
$ date > foo.2
$ # this should fail
$ ( mv -n foo.1 foo.2 && [ ! -f foo.1 ] ) || echo fail
fail
$ rm foo.2
$ # this should succeed
$ ( mv -n foo.1 foo.2 && [ ! -f foo.1 ] ) || echo fail
$ exit

GNUでは、どちらのユーティリティも希望どおりに機能しません。 mvの同じ回避策はUbuntuでも機能します。つまり、GNU cpは、残りの1つの問題として残ります。

余談ですが、cpを呼び出す前に宛先ファイルをテストする際の競合状態についてのコメントを聞きましたが、cpが正しいこと。機会の窓はもっと小さいかもしれませんが、私の直感はそれがまだそこにあるだろうということです。 IANAE、しかし。

mvの回避策は両方のプラットフォームで機能するため、おそらくこの回避策で十分です。

$ rm -f foo.*
$ date > foo.1
$ date > foo.2
$ # this should fail
$ ( cp -n foo.1 TEMPFILE && mv -n TEMPFILE foo.2 && [ ! -f TEMPFILE ] ) || echo fail
fail
$ rm -f TEMPFILE
$ rm foo.2
$ # this should succeed
$ ( cp -n foo.1 TEMPFILE && mv -n TEMPFILE foo.2 && [ ! -f TEMPFILE ] ) || echo fail
$ rm -f TEMPFILE
$ exit
0
Jim L.