mv:宛先が存在しない場合にのみファイルを移動します


44

私は使用することができmv file1 file2、それが唯一動くような方法でfile1file2場合file2は存在しないのですか?

私はもう試した

yes n | mv -i file1 file2

(これによりmv、file2をオーバーライドするかどうかを尋ねて、自動的にnoと答えることができます)しかし、-iそれを悪用するだけでなく、素敵なエラーコードも与えません(移動した場合は常に0ではなく141、移動しない場合は何か)


3
あなたは持っている必要がありますpipefail141の終了ステータスになりますようにオプションyesではなく、mvここにSIGPIPEを取得する理由はないでしょう。
ステファンシャゼル

file2がディレクトリの場合、このアプローチも失敗します(file1をfile2ディレクトリに移動します)。GNU mvには-Tそれがあります。
ステファンシャゼル

StéphaneChazelas@欲求はの終了ステータスを使用する場合mvよりも、むしろをyes、最も簡単な解決策があるかもしれないmv -i file1 file2 < <(yes n)
kasperd

回答:


63

mv -vn file1 file2。このコマンドはあなたが望むことをします。-v必要に応じてスキップできます。

-v 冗長にします-mvは、ファイルを移動するとファイルを移動したことを通知します(ファイルは移動されない可能性があるため便利です)

-n file2が存在しない場合にのみ移動します。

こと、しかし、注意してください、これはPOSIXではないようThomasDickey言及


2
ただし、POSIXではありません。
トーマスディッキー

1
@ThomasDickeyは、POSIXがこれをまったくアトミックな方法でサポートしていますか?
ファビアンシュミテナー

3
to @Fabian:おそらくそうではありませんが、提案された回答の中にも、ツールの記述方法によってはツール内で競合が発生する可能性があります。
トーマスディッキー

3
これはレースフリーでstraceはないようで、(私のシステムで)stat( "file2"、0x7ffe3e705d10)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)lstat( "file1"、{st_mode = S_IFREG | 0644、 st_size = 0、...})= 0 lstat( "file2"、0x7ffe3e705a10)= -1 ENOENT(そのようなファイルまたはディレクトリはない)rename( "file1"、 "file2")= 0 lseek(0、0、SEEK_CUR) = -1 ESPIPE(シークが無効)。そのため、名前変更が使用されているようです。@StéphaneChazelasソリューションは、本当に無料でやりたい場合に最適なソリューションのようです。
ファビアンシュミットナー

2
なぜそれを使用しないのだろうかrenameat2
ファビアンシュミットナー

16

mv -n

man mvGNUシステム上:

-n、-no-clobber
は既存のファイルを上書きしません

FreeBSDシステムの場合:

-n既存のファイルを上書きしないでください。(-nオプションは、以前の-fまたは-iオプションをオーバーライドします。)


10
if [ ! -e file2 ] && [ ! -L file2 ]
then
    mv file1 file2
# else echo >&2 there is already a file2 file.
fi

または:

if ! ls -d file2 > /dev/null 2>&1
then
    mv file1 file2
fi

存在しないmv場合にのみ実行されfile2ます。それがいることを保証するものではありませんfile2ので、上書きされなくなりfile2、テストとの間に作成されている可能性がmvGNUの少なくとも現在のバージョンであることが、注目すべきmvとの-iまたは-n競合状態が狭いですが、その保証のいずれかを(与えることはありません。チェックはmv)内で行われるため)

一方、移植性があり、ケースを区別することができ、file2ファイルのタイプ(通常、パイプ、さらにはディレクトリ)に関係なく動作します。


3
これは、存在チェックと移動の間にファイルを書き込むことができる競合状態をもたらしますか?
ファビアンシュミテナー

3
何をするにしても常に可能性。
マジェンコ

3
Linux APIにはフラグrenameat2を付けることができるものがありますRENAME_NOREPLACE。これはファイルの存在をアトミックにチェックしてからファイルを移動すると思います。
ファビアンシュミテナー

ディレクトリの場合は-d、リンクの場合は-l、または任意のファイルタイプの場合も-e
Majenko

名前の変更は競合しない場合がありますが、mvコマンドの残りの部分はそうではありません。リンクを解除する必要がないと考えると、突然名前の変更に失敗します(エラーが発生するはずです)。
マジェンコ

8

GNUがln提供する競合のないアプローチfile1は、ディレクトリタイプではありません。

ln -PT file1 file2 && rm file1

何のことを保証すること、(いくつかのネットワーク・ファイル・システムのバグを除く)file2ファイルが上書きされませんれます(または場合は、そのfile2タイプのディレクトリであり、file1そこに移動されませんが)、ので、link()システムコール、に反しrename()ている場合、システムコールは失敗しますターゲットが存在します。

しかし、ファイルの両方として存在する中間状態が存在することになるfile1file2

-T(常に行うためのオプションlink("file1", "file2")場合でも、file2型ディレクトリである)は、GNU固有です。

link次のコマンドも使用できます。

link file1 file2 && rm file1

ただし、if file1がシンボリックリンクの場合、実装に応じて、file2そのシンボリックリンクまたはそのシンボリックリンクのターゲットへのハードリンクになります(Solarisでは/usr/sbin/link、ではなくを使用します/usr/xpg4/bin/link)。


2
renameat2フラグ付きのLinux API RENAME_NOREPLACEがアトミックかどうか知っていますか?
ファビアンシュミテナー

1
@Fabian、意図したとおりですが、非常に新しく、すべてのファイルシステムでサポートされているわけではありません。今後、Linuxでの将来のmv実装がそれを使用することを期待できます。それはそれが設計されたものです。
ステファンシャゼル

0

test -e name(ファイル、ディレクトリ、またはシンボリックリンクに関係なく)名前が存在する場合にtrueを返すwhich も使用できます。

例えば:

touch file
mkdir dir
ln -s file symlink
test -e file && echo file exists
test -e dir && echo dir exists
test -e symlink && echo symlink exists
test -e file || echo you wont see this echo
test -e doesnotexist || echo doesnotexist does not exist...

1
しかしln -s doesnotexist exists; test -e exists || echo "does it really not exist?"。たとえば、同じln -s /var/spool/cron/crontabs/. existsです(また、rootまたはcrontabグループのメンバーではありません)。
ステファンシャゼル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.