意外将文件移动到不存在的目录会删除文件吗?

意外将文件移动到不存在的目录会删除文件吗?

我使用从一个目录移动了所有文件mv,但在目标位置路径中意外输入了一个错误。

系统返回一条消息,称目录不存在,但源目录中的文件已被删除。

这是错误吗?将文件移动到不存在的位置是否会删除正在移动的文件?(这是在 Ubuntu 18.04.2 LTS 上。)


具体如下:

  1. 已创建test.txt文件。
  2. 将文件/ben移至sudo
  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

答案1

在您实际运行的命令中,您没有丢失任何东西!它成功重命名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

以下是有关您问题中描述的一般情况的信息(关于因尝试移动文件而丢失文件)以及可能出现和不会出现的问题。


作为林兹温德 说,当您尝试使用单个命令将文件移动到不存在的目标目录时,不会导致数据丢失mv。但如果您运行mv多次,例如在 shell 循环中,则可能会发生这种情况。

例如,假设我有:

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要将所有这些文件移到应该在类似或mv的命令中,将所有名称传递给。无论哪种情况(即,无论我是否在目标目录名称后面加上斜杠),这都是正确的。无论哪种情况,如果我拼错了目标目录名称(例如,改为写),我都会收到错误,并且不会丢失任何数据。mv file*.txt dest/mv file*.txt destdstmv: target 'dst' is not a directory

但是,假设我拼错了dst,省略了结尾的/,并运行了多个mv命令。那就糟糕了,因为当 的目标mv是常规文件时,mv 取代它!

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默默地不是覆盖。否则,mv仅在目标文件是只读文件时才在覆盖前询问您。考虑这一点的一个原因是它涵盖了其他情况,例如mv file01.txt dest/您没有意识到dest/file01.txt存在并且不想覆盖它的情况。

您还可以在命令末尾使用-t dest而不是,例如 。如果是常规文件,则无论您是否在命令末尾写入,都将拒绝操作。destmv -t dest file*.txtdest/


使用自动化机制运行多个此类命令可能会严重加剧问题。例如,如书面该命令for f in file*.txt; do mv "$f" dest/; done虽然复杂,但却很安全,因为如果我不小心指定了文件dst而不是目录dest(但保留了斜线!),它会为mv: failed to access 'dst/': Not a directory每个文件给出一个错误。但是,如果我省略了结尾的/,那么它会将每个文件重命名为dst替换前一个文件dst,并且只保留最后一个文件。

类似的糟糕结果也会出现find,包括或许使用起来合理find(但有所不同,并且仍然要格外小心)。例如,假设我想移动file*.txt整个目录树中与 glob 匹配的所有文件(dest除了本身之外) 进入目录dest。我可能首先想到的是使用这个:

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

因为我在结尾处添加了一个/,所以写的是dest/而不是,即使我写的是dest,也不会覆盖名为 的文件。但它有一个相关的问题,即如果目录树不同部分的文件具有相同的名称,它将覆盖已复制的文件。例如,如果有 和,则一个将覆盖另一个。为了避免这种情况,最好使用类似这样的方法:dstdstdesta/file01.txtb/file01.txt

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

另一个好处-t dir是,因为它允许您指定目标目录被移动的项目,它与+的形式兼容-exec,其中多个项目被传递给一个命令,从而运行更少的命令(通常只有一个):

find -path ./dest -prune -o -name 'file*.txt' -exec mv -it dest/ {} +  # good

在这两种情况下(除了\;vs之外,它们是相同的+),我还传递了-i在每个会覆盖文件的操作之前提示的选项。如果您只是想默默地跳过这些操作,请写入n而不是i。如果您想先测试find命令,您可以在命令的其余部分echo之后-exec但在命令之前写入以打印将要运行的内容。例如:

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。)

答案2

不,您建议的应该不可能。您可能需要更好地了解目标。使用history获取以前发出的命令的列表。

有几件事:

  • 只要目标位置与源位于同一分区,数据就不会移动。只有目录条目中的名称会更改。

如果确实有举动...

  • 参见info coreutils 'mv invocation'(在线版本https://www.gnu.org/software/coreutils/manual/html_node/mv-invocation.html#mv-invocation)了解 mv 的工作原理以及更具体地说这部分:

    它首先使用与“cp -a”相同的代码来复制请求的目录和文件,然后(假设复制成功)删除原始文件。如果复制失败,则复制到目标分区的部分将被删除。如果您将三个目录从一个分区复制到另一个分区,并且第一个目录的复制成功,但第二个目录的复制失败,则第一个目录将留在目标分区上,第二个和第三个目录将留在原始分区上。

  • 因此一步棋由两部分组成:

    1. Acp -a
    2. Amv

    在确认复制已正确完成后,移动的删除部分才完成。

  • 如果你的 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/ 不存在,所以会出错。

相关内容