我使用从一个目录移动了所有文件mv
,但在目标位置路径中意外输入了一个错误。
系统返回一条消息,称目录不存在,但源目录中的文件已被删除。
这是错误吗?将文件移动到不存在的位置是否会删除正在移动的文件?(这是在 Ubuntu 18.04.2 LTS 上。)
具体如下:
- 已创建
test.txt
文件。 - 将文件
/ben
移至sudo
。 - 文件消失了。
/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 dest
dst
mv: 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
而不是,例如 。如果是常规文件,则无论您是否在命令末尾写入,都将拒绝操作。dest
mv -t dest file*.txt
dest
/
使用自动化机制运行多个此类命令可能会严重加剧问题。例如,如书面该命令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
,也不会覆盖名为 的文件。但它有一个相关的问题,即如果目录树不同部分的文件具有相同的名称,它将覆盖已复制的文件。例如,如果有 和,则一个将覆盖另一个。为了避免这种情况,最好使用类似这样的方法:dst
dst
dest
a/file01.txt
b/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”相同的代码来复制请求的目录和文件,然后(假设复制成功)删除原始文件。如果复制失败,则复制到目标分区的部分将被删除。如果您将三个目录从一个分区复制到另一个分区,并且第一个目录的复制成功,但第二个目录的复制失败,则第一个目录将留在目标分区上,第二个和第三个目录将留在原始分区上。
因此一步棋由两部分组成:
- A
cp -a
- A
mv
在确认复制已正确完成后,移动的删除部分才完成。
- A
如果你的 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/ 不存在,所以会出错。