mv
を使用して1つのディレクトリからすべてのファイルを移動し、誤ってターゲットの場所のパスにタイプミスをしました。
システムはディレクトリが存在しないというメッセージを返しましたが、ソースディレクトリからのファイルが消去されました。
それはバグですか?ファイルを存在しない場所に移動すると、移動中のファイルが消去されますか? (これはUbuntu 18.04.2 LTSにあります。)
詳細は次のとおりです。
test.txt
ファイルを作成しました。Sudo
付きで/ben
に移動しました。/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
実際に実行したコマンドでは、何も失われていません! _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
の宛先が通常のファイルである場合、mv
replacesそれなので、それは悪いことです!
_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
_エラー。ただし、末尾の_/
_を省略した場合、各ファイルの名前はdst
、replacing以前の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
_です。)
いいえ、あなたが提案することは不可能であるべきです(!)おそらく、目的地をよく見る必要があります。以前に発行されたコマンドのリストを取得するには、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つの部分で構成されます。
cp -a
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 /が存在しないため、エラーになります。