Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I accidentally forgot to specify destination before hitting the Return key. Where does mv ./* without specifying destination move the files and directories under current directory to?

share|improve this question
1  
2  
"I'm surprised that mv accepts 1 argument" It doesn't. It accepts all arguments the shell passed to it after expanding the *. –  glglgl Aug 22 at 7:48

5 Answers 5

up vote 24 down vote accepted

If the last argument was a directory, you just moved all of the files and directories in your current working directory (except those whose names begin with dots) into that directory. If there were two files, the first file may have overwritten the second file.

Here are some demonstrations:

More than two files and the last argument is a file

$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory

More than two files and the last argument is a directory

$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'

Two files

$ touch a b
$ mv -v *
'a' -> 'b'

Further explanation

The shell expands the glob (*) into arguments for mv. The glob is usually expanded in alphabetical order. mv always sees a list of files and directories. It never sees the glob itself.

The command mv supports two types of moving. One is mv file ... directory. The other is mv old-file-name new-file-name (or mv old-file-name directory/new-file-name).

share|improve this answer

First I'll make a test base - 5 files and one folder:

touch file1 file2 file3 file4 file5
mkdir folder

Next I'll run a test command. The -v option specifies that I want every command the shell executes to be printed to stderr. The -x option specifies that I want the same printed to stderr - but I want it done after the command is evaluated but before the shell runs it.

sh -cxv 'echo mv *'

OUTPUT

echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder

So you see that the command I feed the shell is echo mv * and the command the shell executes after * is expanded is echo mv followed by all of those files and the folder.

By default the shell will expand globs like:

sh -cxv 'echo file[1-5]'

OUTPUT

echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5

This is a result of the set [+-]f glob function:

sh -cxvf 'echo file[1-5]'

OUTPUT

echo file[1-5]
+ echo 'file[1-5]'
file[1-5]

So when you run a command in a shell configured with default options like mv * the shell expands into the * word an argument list of all files in the current directory sorted according to locale. It does the syscall exec(ve) for mv (essentially) with this argument list appended. So mv gets all of the arguments as the shell globs them and sorts them. Besides doing strace to see these effects, you can use the debug out again like:

sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT

OUTPUT

sh\000-s\000--\000mv\000file1\000file2\000file3\000file4\000file5\000folder\
\000$
mv file1 file2 file3 file4 file5 folder

And portably:

( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1

OUTPUT

: /mv/file1/file2/file3/file4/file5/folder/

Basically, the shell executes mv with the contents of the directory (if it is not empty and not including files/folders with names beginning with .) as its argument list. mv is POSIX specified to interpret its final argument as a directory if it is invoked with more than two arguments - in the same way ln is (because, in fact, they're incredibly similar tools in underlying function).

Enough echoes though:

sh -cxv 'mv *' ; ls

OUTPUT

mv *
+ mv file1 file2 file3 file4 file5 folder
folder/

All of the files were moved into the final argument - because it is a folder. Now what if it is not a folder?

sh -cxv 'cd *; mv *'; ls . *

OUTPUT

cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory

.:
folder/

folder:
file1  file2  file3  file4  file5

This is how POSIX specifies mv should behave in that case:

mv [-if] source_file target_file

mv [-if] source_file... target_dir

In the first synopsis form, the mv utility shall move the file named by the source_file operand to the destination specified by the target_file. This first synopsis form is assumed when the final operand does not name an existing directory and is not a symbolic link referring to an existing directory. In this case, if source_file names a non-directory file and target_file ends with a trailing /slash character, mv shall treat this as an error and no source_file operands will be processed.

In the second synopsis form, mv shall move each file named by a source_file operand to a destination file in the existing directory named by the target_dir operand, or referenced if target_dir is a symbolic link referring to an existing directory. The destination path for each source_file shall be the concatenation of the target directory, a single /slash character if the target did not end in a /slash, and the last pathname component of the source_file. This second form is assumed when the final operand names an existing directory.

So if * expands to:

  • two files

    • You should have only one file, which is the first renamed to the second after the second is unlinked.
  • one or more files followed last by a directory or a link to one

    • You should have only one directory or a link to one, which is where all its parent's previous contents have just been moved.
  • anything else

    • You should have an error message and a satisfying sigh of relief.
share|improve this answer
1  
yes, using the old sh thingy, I forgot about debugging with that, but regarding my original question, it means that the problem is the shell not mv ? That's nasty, it's simpler than I originally thought but it's a real trap . –  user2485710 Aug 22 at 3:38
    
So the current directory should not actually be empty, but contain one directory that holds the original contents of the current directory? What would happen if the final expansion by the shell is a file rather than a directory? –  Warwick Aug 22 at 3:38
4  
@user2485710, the key point is that in Unix, the shell is responsible for expanding wildcards before passing the command line arguments to the command. In Windows, each command has to expand wildcards itself. This allows Unix shells to offer advanced wildcard facilities that work with any command. –  cjm Aug 22 at 3:41
2  
@mikeserv given the fact that those files were not that important I rushed myself into cleaning up and skip to the next step, but I can confirm things as they now appear in my edit of my first post/question. –  user2485710 Aug 22 at 4:20
6  
@mikeserv tl;dr, * is not a single argument? Right? :) –  Bernhard Aug 22 at 6:09

First the shell expands ./* to all files in the current directory (except files starting with a dot).

  • if there is only one file: mv fails
  • if there are two file: the first one is moved to the second (which therefore get lost)
  • if there are more than two files:
    • if the last one is a directory: all files are moved into this directory
    • otherwise mv fails.
share|improve this answer
1  
Thanks. how to tell which subdirectory is " the last one"? –  Tim Jul 23 '13 at 21:58
7  
Just call echo ./* do see which order your shell uses (usually alphabetical). –  jofel Jul 23 '13 at 22:00

When you type mv ./*, your shell will expand ./* before executing mv.

A few things to note:

  • If ./* is expanded into less than 2 arguments, mv will, logically, produce an error.
  • ./* will usually expand into every file (including directory) present in the current directory and not starting with a dot.
  • You can control what ./* expands into by reading the documentation of your shell (search for "globbing" in the documentation/man). Different shells will have different options.
share|improve this answer

What does mv * do?

Here's a shorter answer:

The shell expands the wildcard * to a list of directory contents. Then the shell passes that full list to the command. The command never sees *.

The command mv file1 file2 ... filen directory will move file1 ... filen into directory.

Example

Here I make a test directory containing three files

$ mkdir t
$ cd t
$ echo a>a; echo b>b; echo c>c
$ ls
a  b  c

You can't move multiple files into a single file

$ mv *
mv: target `c' is not a directory

Let's add a subdirectory

$ mkdir d

You can move multiple files into a subdiretory

$ mv *
$ ls
d
$ ls d
a  b  c
share|improve this answer
    
The command mv file1 file2 ... filen directory is very little likely to have anything at all to do with *. –  mikeserv Aug 25 at 13:11
    
@Mike: My answer points out that the last stage of shell expansion effectively transforms the latter into the former. Not knowing this appears to be the root of the OP's confusion. –  RedGrittyBrick Aug 25 at 14:11
    
yes, but my point is the shell would not expand * into file dir unless there is some locale in which d follows f. –  mikeserv Aug 25 at 14:50
    
@mikeserv: There is such a locale, my example is a cut & paste from Putty connecting to a CentOS 5.6 GNU/Linux system. –  RedGrittyBrick Aug 25 at 15:17
    
which locale is that? your example is a b c d which isn't file ... dir. I only commented at all because you don't mention the sort anywhere and say only that * becomes file ... dir which doesn't happen. –  mikeserv Aug 25 at 16:30

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.