web-dev-qa-db-ja.com

存在しないディレクトリに誤ってファイルを移動すると、ファイルが消去されますか?

mvを使用して1つのディレクトリからすべてのファイルを移動し、誤ってターゲットの場所のパスにタイプミスをしました。

システムはディレクトリが存在しないというメッセージを返しましたが、ソースディレクトリからのファイルが消去されました。

それはバグですか?ファイルを存在しない場所に移動すると、移動中のファイルが消去されますか? (これはUbuntu 18.04.2 LTSにあります。)


詳細は次のとおりです。

  1. test.txtファイルを作成しました。
  2. ファイルをSudo付きで/benに移動しました。
  3. ファイルが消えました。 /benは存在しません。

コマンドと出力は次のとおりです。

ben.b@c-w46:~/Desktop/test-folder$ Sudo mv test.txt /ben
ben.b@c-w46:~/Desktop/test-folder$ cd /ben
bash: cd: /ben: Not a directory
8
ben berizovsky

実際に実行したコマンドでは、何も失われていません! _test.txt_を_/ben_に名前変更することに成功しました。 _test.txt_が通常のファイルであると仮定すると、新しい_/ben_もそうです(結局、これらは同じファイルです)。

_bash: cd: /ben: Not a directory_が表示されている理由は、缶に記載されているものです:_/ben_はディレクトリではありません。ファイルには引き続きアクセスできます。

そのような間違いを回避し、宛先がディレクトリでない場合にmvを強制的に失敗させたい場合は、末尾に_/_を書き込むか、_-t dir_を使用します。たとえば、これらはいずれも、(必要に応じてSudoを使用して)経験した(非常に小さな)問題を回避します。

_mv test.txt /ben/  # no action, unless `/ben` is an existing directory
_
_mv -t /ben test.txt  # same deal, -t doesn't accept regular-file operands
_
_mv -t /ben/ test.txt  # you can even do both if you want
_

質問で説明されている一般的な状況(ファイルを移動しようとするとファイルが失われること)に関する情報と、問題の原因と不可能な原因について説明します。


Rinzwindによる のように、存在しない宛先ディレクトリにファイルを移動しても、単一のmvコマンドを使用してデータを損失することはありません。ただし、Shellループなどでmvを複数回実行した場合に発生する可能性があります。

たとえば、私が持っていると仮定します:

_ek@Apok:~/tmp$ ls -F
dest/       file02.txt  file04.txt  file06.txt  file08.txt  file10.txt
file01.txt  file03.txt  file05.txt  file07.txt  file09.txt
_

これらのファイルをすべてdestに移動するには、私はshould _mv file*.txt dest/_または_mv file*.txt dest_のようなコマンドで、すべての名前をmvに渡します。どちらの場合でも、つまり、ターゲットディレクトリ名に末尾にスラッシュを付けるかどうかにかかわらず、これは正しいことです。どちらの場合も、ターゲットディレクトリ名のスペルを間違えると(たとえば、dstと書くことで)、エラー_mv: target 'dst' is not a directory_が発生し、データが失われることはありません。

ただし、dstのスペルを間違え、末尾の_/_を省略して、複数のmvコマンドを実行するとします。 mvの宛先が通常のファイルである場合、mvreplacesそれなので、それは悪いことです!

_ek@Apok:~/tmp$ mv file01.txt dst  # bad if dst exists but isn't a directory
ek@Apok:~/tmp$ mv file02.txt dst  # bad, I just lost the old file01.txt!
_

これが、多くの人が常にmvの末尾に_/_を付けて宛先ディレクトリーを書き込むことを好む理由です。

_ek@Apok:~/tmp$ mv file03.txt dst/
mv: failed to access 'dst/': Not a directory
_

_mv -i_を使用して上書きする前に確認するか、_mv -n_を使用してサイレントにnotを上書きできます。それ以外の場合、mvは、宛先が読み取り専用ファイルであるかどうかを上書きする前に確認するだけです。これを検討する1つの理由は、_mv file01.txt dest/_が存在し、それを上書きしたくないことに気づかなかった_dest/file01.txt_などの他のケースをカバーすることです。

コマンドの最後にdestと書く代わりに_-t dest_を使用することもできます(例:_mv -t dest file*.txt_)。末尾の_/_を書き込んだかどうかに関係なく、destが通常のファイルである場合、これは動作を拒否します。


自動化されたメカニズムを使用して複数のそのようなコマンドを実行すると、問題が深刻に悪化する可能性があります。たとえば、書かれているコマンド_for f in file*.txt; do mv "$f" dest/; done_は不必要に複雑ですが安全です。ディレクトリdstではなく誤ってファイルdestを指定した場合(スラッシュは保持されます!)、ファイルごとに1つの_mv: failed to access 'dst/': Not a directory_エラー。ただし、末尾の_/_を省略した場合、各ファイルの名前はdstreplacing以前のdstに変更され、最後のファイルのみが残ります。

同様の悪い結果は、findを使用して達成できます。これには、findの使用が適切な場合があります=可能性があります(ただし、異なる方法で、さらに注意が必要)たとえば、ディレクトリツリー全体( dest自体を除く )の_file*.txt_グロブに一致するすべてのファイルをディレクトリdestに移動したいとします。私は最初にこれを使うと思うかもしれません:

_find . -path ./dest -Prune -o -name 'file*.txt' -exec mv {} dest/ \;  # bad, don't use
_

末尾に_/_を含め、destの代わりに_dest/_を書き込んだため、dstの代わりにdstを書き込んでも、destというファイルは上書きされません。ただし、ディレクトリツリーの別の部分にあるファイルに同じ名前が付いている場合、すでにコピーしたファイルを上書きするという関連する問題があります。たとえば、_a/file01.txt_と_b/file01.txt_がある場合、一方が他方を上書きします。それを避けるためにも、次のようなものを使用することをお勧めします。

_find -path ./dest -Prune -o -name 'file*.txt' -exec mv -it dest/ {} \;  # okay
_

_-t dir_のもう1つの利点は、移動先ディレクトリを指定できるためですbefore移動するアイテムは_+_形式の_-exec_と互換性があります。コマンドに複数のアイテムが渡されるため、実行されるコマンドが少なくなります(多くの場合1つのみ)。

_find -path ./dest -Prune -o -name 'file*.txt' -exec mv -it dest/ {} +  # good
_

どちらの場合も(_\;_と_+_を除いて同じです)、ファイルを上書きする各操作の前に_-i_オプションをプロンプトに渡しました。黙ってそれらをスキップしたい場合は、nの代わりにiを記述してください。 findコマンドを最初にテストする場合は、_-exec_の後、コマンドの残りの前にechoを記述して、実行される内容を出力できます。例えば:

_ek@Apok:~/tmp$ find -path ./dest -Prune -o -name 'file*.txt' -exec echo mv -it dest/ {} +
mv -it dest/ ./file02.txt ./file06.txt ./file10.txt ./file09.txt ./file01.txt ./file04.txt ./file05.txt ./file07.txt ./file03.txt ./file08.txt
_

(もちろん、これは私が示した元のディレクトリにあります。ここでは、移動するすべてのファイルが同じ場所にあるため、findはやりすぎであり、使用する最も複雑な合理的なコマンドは_mv -it dest/ file*.txt_です。)

14
Eliah Kagan

いいえ、あなたが提案することは不可能であるべきです(!)おそらく、目的地をよく見る必要があります。以前に発行されたコマンドのリストを取得するには、historyを使用します。

いくつかのこと:

  • ターゲットの場所がソースと同じパーティションにある限り、データは移動されません。ディレクトリエントリの名前のみが変更されます。

ある場合IS移動完了...

  • 見る info coreutils 'mv invocation'(オンラインバージョン https://www.gnu.org/software/coreutils/manual/html_node/mv-invocation.html#mv-invocation )mvがどのように機能するか、より具体的には部:

    まず、「cp -a」で使用される同じコードの一部を使用して、要求されたディレクトリとファイルをコピーし、次に(コピーが成功したと仮定して)元のコードを削除します。コピーが失敗すると、宛先パーティションにコピーされた部分が削除されます。あるパーティションから別のパーティションに3つのディレクトリをコピーする場合、最初のディレクトリのコピーは成功したが2番目のディレクトリは成功しなかった場合、最初のディレクトリはコピー先パーティションに残り、2番目と3番目は元のパーティションに残ります。

  • したがって、移動は2つの部分で構成されます。

    1. cp -a
    2. mv

    移動の削除部分は、コピーが正しく行われたことが確認された後に行われます。

  • mvが複数のファイルで構成されている場合、コピーと移動は中間で行われます。だから

    mv a b c d e f /dir/
    

    します

    cp a /dir/
    rm a
    ...
    cp f /dir/
    rm f
    

    したがって、aとfの間に問題がある場合は、問題が発生した場所までのaの移動が終了します。これは、ワイルドカードの使用にも適用されます。


編集について

Sudo mv test.txt /ben

これはtest.txtを/に移動し、名前をbenに変更します。そして

ben.b@c-w46:~/Desktop/test-folder$ cd /ben
bash: cd: /ben: Not a directory

正しくエラーが発生します。する

ls -l /ben

ファイルが表示されます。

ファイルをディレクトリに移動する場合は、常に/を追加する必要があります。

Sudo mv test.txt /ben/

/ ben /が存在しないため、エラーになります。

8
Rinzwind