When you ran it in that directory, mv *
was equivalent to mv 1 2 3 4 a b c
. Unless the -t
option is present, mv
treats the last non-option argument as the name of the destination, and thus moved 1
, 2
, 3
, 4
, a
, and b
all into c
.
The shell expands *
into a list of filenames. When this happens, the command you are running doesn't actually see *
, but only what it expanded to. The command you run therefore cannot make judgments about how to treat its arguments based on what you actually typed--it only sees what the shell passes to it.
When you run mv *
--which, as you have probably inferred, should almost always be avoided--the shell and not the mv
command itself expands *
into an alphabetized list of all files and directories contained in the current directory, excluding any whose names start with .
.
(You can customize how that works through the dotglob
, nullglob
, and nocaseglob
shell options and the GLOBIGNORE
variable. See the documentation for details.)
If c
weren't a directory, your command would fail, move nothing, and show this error message:
mv: target 'c' is not a directory
But since c
was a directory, it simply "worked," even though--if you were running the command for any purpose other than to see what it would do--you probably intended to write the name of a separate directory at the end of the command after the *
.
To show more intuitively why mv
has no way of knowing that its arguments were expanded from *
, suppose you had written:
mv 1 c
That would move 1
to c
. But maybe you would have liked to move two items to c
:
mv 1 2 c
Or five items:
mv 1 2 3 4 a c
Or six items:
mv 1 2 3 4 a b c
But the shell ensured that mv *
passed exactly the same arguments in the same order to the mv
command as mv 1 2 3 4 a b c
.
It would often be inconvenient not to be able to move multiple items with a single invocation of mv
. But even if this weren't allowed and always produced an error, you could still create an analogously confusing situation in a directory that contained just two entries. Then running mv *
in that directory would still try to move one to the other.
If you're wondering what the shell will expand something to, you can replace the command you are interested in running with printf '%s\n'
, which prints all the subsequent arguments passed to it, separated by line breaks. For example:
printf '%s\n' *