2

I had a directory structure like this:

ravikumar@ravikumar-RV409-RV509-RV709:~$ tree test
test
|
├── 1
├── 2
├── 3
├── 4
├── a
├── b
└── c

I executed mv command as:

ravikumar@ravikumar-RV409-RV509-RV709:~$ cd test/
ravikumar@ravikumar-RV409-RV509-RV709:~/test$ mv *

Result:

ravikumar@ravikumar-RV409-RV509-RV709:~/test$ tree
.        
|
└── c
    ├── 1
    ├── 2
    ├── 3
    ├── 4
    ├── a
    └── b

My question here is how does mv judge move all directories and files to the last directory? and that is c.

Eliah Kagan
  • 117,780

2 Answers2

6

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' *
Eliah Kagan
  • 117,780
1

This answer is adding some tips (but I think the main answer is that by @Eliah Kagan).


I made two aliases to make things a little safer. I store them in ~/.bashrc near the other aliases.

alias mv='mv -i'
alias rm='rm -i'

These aliases make the commands interactive, they ask if you want to overwrite/remove.

mv will ask if you want to overwrite the target, if it exists and is a file, or if the target is a directory and there is already a file with the same name in it. This will help you avoid mistakes, for example when you use wild cards, mv *.


Please notice

  • These aliases work only with your own user ID. They do not work with sudo. If you want aliases to be expanded after sudo, you must set that up separately. If you want a particular alias to work in a root shell (like you would get from sudo -s or sudo -i) then you must create a similar alias and store it in /root/.bashrc.

  • You can override the aliases (and get the original behaviour of mv and rm) with the prefix 'backslash`,

    \rm filename
    \mv filename1 filename2
    
  • You may get accustomed to the -i flag being passed automatically, and have a false sense of safety, for example when running another system, where these aliases are not active.

sudodus
  • 46,324
  • 5
  • 88
  • 152