Hi Ted,

Ted Unangst wrote on Sun, Dec 26, 2010 at 12:39:16AM -0500:

> A 3 line example with 3 bugs.
> 
> 1.  destination and destination_path should be the same.

Sure, and both should be "destination_path", because that's the
term used consistently in the DESCRIPTION.

Unambiguous terminology is very important here because "target"
and "destination path" are not the same thing at all, but easily
confused.  At first, when starting to look into your bug report,
i confused them as well, so let's try to be as precise as possible.

> 2.  source_file doesn't have to be a file.

Right, and indeed, it is the same as the source operand.

By the way, the DESCRIPTION ought to make it plain as well
that sources can be directories.

> 3.  mv will also unlink a directory, ala rm -d.

Indeed, the code does contain a call to rmdir(2).
However:

  ischwa...@isnote $ df . /tmp
  Filesystem  512-blocks      Used     Avail Capacity  Mounted on
  /dev/wd0g     41280412  31471652   7744740    80%    /home
  /dev/wd0d      2061100        20   1958028     0%    /tmp
  ischwa...@isnote $ touch source
  ischwa...@isnote $ mkdir -p /tmp/target/source
  ischwa...@isnote $ mv source /tmp/target
  mv: rename source to /tmp/target/source: Is a directory
  ischwa...@isnote $ ls -F /tmp/target
  source/

Thus, files do not overwrite directories.

  ischwa...@isnote $ rm -df source /tmp/target/source
  ischwa...@isnote $ mkdir source
  ischwa...@isnote $ touch /tmp/target/source
  ischwa...@isnote $ mv source /tmp/target            
  mv: rename source to /tmp/target/source: Not a directory
  ischwa...@isnote $ ls -F /tmp/target                
  source

Thus, directories do not overwrite files.

  ischwa...@isnote $ rm -df source /tmp/target/source 
  ischwa...@isnote $ mkdir source                     
  ischwa...@isnote $ mkdir -p /tmp/target/source      
  ischwa...@isnote $ touch /tmp/target/source/file
  ischwa...@isnote $ mv source /tmp/target            
  mv: can't remove /tmp/target/source: Directory not empty
  ischwa...@isnote $ ls -F /tmp/target/source         
  file

Thus, the only case of actual rmdir(2) is removing an empty
directory to replace it with a new directory.

Just adding the -d here to the rm statement provokes the
misunderstanding that you can move a file on top of an existing
directory.  Thus, when adding the -d, we should make it clear
which moves never take effect.  That should be said before
the options, because not even -f forces those moves.

Gah, i just see the CAVEATS.  That is easy to overlook, and
it is not just a CAVEAT, it is documenting how the utility works.
Besides, the CAVEAT is not even complete:  It also applies to the
first SYNOPSIS form, it is lacking the case of non-empty
directories, and it doesn't say what the consequences are.
My new text is both more precise and shorter.

Finally, as mv.c actually execl(3)s cp(1) and rm(1), why not
report the real commands executed, instead of leaving out
an option that *is* passed?

So, what about the following:

Yours,
  Ingo


Index: mv.1
===================================================================
RCS file: /cvs/src/bin/mv/mv.1,v
retrieving revision 1.26
diff -u -r1.26 mv.1
--- mv.1        3 Sep 2010 09:53:20 -0000       1.26
+++ mv.1        26 Dec 2010 17:18:08 -0000
@@ -38,7 +38,7 @@
 .Os
 .Sh NAME
 .Nm mv
-.Nd move files
+.Nd move files and directories
 .Sh SYNOPSIS
 .Nm mv
 .Op Fl fi
@@ -49,7 +49,7 @@
 .Sh DESCRIPTION
 In its first form, the
 .Nm
-utility moves the file named by the
+utility moves the file or directory named by the
 .Ar source
 operand to the destination path named by the
 .Ar target
@@ -59,20 +59,27 @@
 .Pp
 In its second form,
 .Nm
-moves each file named by a
+moves each file and directory named by a
 .Ar source
-operand to a destination specified by the
+operand into the destination specified by the
 .Ar directory
 operand.
 It is an error if the
 .Ar directory
-operand does not exist.
+does not exist.
 The destination path for each
 .Ar source
 operand is the pathname produced by the concatenation of the
 .Ar directory
 operand, a slash, and the final pathname component of the named file.
 .Pp
+In both forms, a
+.Ar source
+operand is skipped with an error message
+when the respective destination path is a non-empy directory,
+or when the source is a file, but the destination path is a directory
+or vice versa.
+.Pp
 The options are as follows:
 .Bl -tag -width Ds
 .It Fl f
@@ -117,9 +124,9 @@
 to accomplish the move.
 The effect is equivalent to:
 .Bd -literal -offset indent
-$ rm -f destination_path && \e
-    cp -PRp source_file destination && \e
-    rm -rf source_file
+$ rm -df -- destination_path && \e
+    cp -PRp -- source destination_path && \e
+    rm -rf -- source
 .Ed
 .Sh EXIT STATUS
 .Ex -std mv
@@ -145,6 +152,26 @@
 $ mv -i -- -f bar
 $ mv -i ./-f bar
 .Ed
+.Pp
+If
+.Pa f
+and
+.Pa g
+are files and
+.Pa d
+and
+.Pa d/f
+are directories, the command
+.Pp
+.Dl $ mv f g d
+.Pp
+will print an error message, leave
+.Pa f
+where it is, move
+.Pa g
+to
+.Pa d/g
+and return a non-zero exit status.
 .Sh SEE ALSO
 .Xr cp 1 ,
 .Xr rm 1 ,
@@ -161,20 +188,3 @@
 .Nm
 command appeared in
 .At v1 .
-.Sh CAVEATS
-In the second synopsis form
-if the destination path exists,
-the
-.Ar source
-operand and the destination path
-must both be a file or must both be a directory
-for the operation to succeed.
-For example, if
-.Pa f
-is a file and
-.Pa d
-and
-.Pa d/f
-are directories, the following command will fail:
-.Pp
-.Dl $ mv f d

Reply via email to