In the command you actually ran, you didn't lose anything! It succeeded at renaming test.txt
to /ben
. Assuming test.txt
was a regular file, so is the new /ben
(they're the same file, after all).
The reason you see bash: cd: /ben: Not a directory
is what it says on the tin: /ben
is not a directory. You can still access the file.
If you want to avoid that sort of mistake and force mv
to fail if the destination isn't a directory, write a trailing /
on it or use -t dir
. For example, any of these would've prevented the (very minor!) problem you experienced (with sudo
as necessary):
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
Information about the general situation described in your question--about losing files by attempting to move them--and what can and can't go wrong follows.
As Rinzwind says, moving files to a nonexistent destination directory shouldn't cause data loss when you attempt it using a single mv
command. But it could happen if you ran mv
more than once, such as in a shell loop.
For example, suppose I have:
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
To move all those files into dest
, I should pass all their names to mv
, in a command like mv file*.txt dest/
or mv file*.txt dest
. In either case--that is, whether or not I write the target directory name with a trailing slash--this does the right thing. And in either case, if I misspell the target directory name (say, by writing dst
instead) I get an error mv: target 'dst' is not a directory
and no data are lost.
However, suppose I were to misspell dst
, omit the trailing /
, and run multiple mv
commands. That would be bad, because when the destination of mv
is a regular file, mv
replaces it!
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!
This is why many people prefer always to write destination directories with a trailing /
in mv
:
ek@Apok:~/tmp$ mv file03.txt dst/
mv: failed to access 'dst/': Not a directory
You can use mv -i
to ask you before overwriting, or mv -n
to silently not overwrite. Otherwise, mv
only ask you before overwriting if the destination is a read-only file. One reason to consider this is that it covers other cases, like mv file01.txt dest/
where you didn't realize dest/file01.txt
existed and didn't want to overwrite it.
You can also use -t dest
instead of writing dest
at the end of the command, e.g., mv -t dest file*.txt
. This refuses to operate if dest
is a regular file, regardless of whether or not you write a trailing /
.
Using an automated mechanism to run multiple such commands can gravely compound the problem. For example, as written the command for f in file*.txt; do mv "$f" dest/; done
is needlessly complicated but safe, because if I accidentally specified the file dst
instead of the directory dest
(but kept the slash!), it would give me one mv: failed to access 'dst/': Not a directory
error per file. However, if I omitted the trailing /
, then it would rename each file to dst
, replacing the previous dst
, and only the last file would remain.
Similar bad outcomes can be achieved with find
, including in situations where it may be reasonable to use find
(but differently, and still with extra care). For example, suppose I wanted to move all files matching the glob file*.txt
in an entire directory tree (except in dest
itself) into the directory dest
. I might first think to use this:
find . -path ./dest -prune -o -name 'file*.txt' -exec mv {} dest/ \; # bad, don't use
Because I included a trailing /
, writing dest/
instead of dest
, this wouldn't overwrite a file called dst
even if I wrote dst
instead of dest
. But it has the related problem that it will overwrite files that it has already copied, if files in different parts of the directory tree have the same name. For example, if there's an a/file01.txt
and a b/file01.txt
, one will overwrite the other. To avoid that, too, it's better to use something like this:
find -path ./dest -prune -o -name 'file*.txt' -exec mv -it dest/ {} \; # okay
The other benefit of -t dir
is that, because it lets you specify the destination directory before the items being moved, it's compatible with the +
form of -exec
, where multiple items are passed to a command, thereby running fewer commands (often just one):
find -path ./dest -prune -o -name 'file*.txt' -exec mv -it dest/ {} + # good
In both cases (they're the same except for \;
vs. +
) I've also passed the -i
option to prompt before each operation that would overwrite a file. If you just want to silently skip those, write n
instead of i
. If you want to test your find
commands first, you can write echo
after -exec
but before the rest of the command to print what would be run. For example:
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
(Of course, that's in the original directory I showed, where all the files to moved are in the same location, and thus where find
is overkill and the most complicated reasonable command to use is mv -it dest/ file*.txt
.)
test.txt
to/ben
, so indeed/ben
is not a directory (it is the newfile.txt
).mv
works./
, as/ben
. You can move it back, for example. I've posted an answer covering that and also the more general topic.