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