mvコマンド の “mv: will not overwrite just-created” メッセージ

2012/03/11Linux

Linux付属のmvコマンドを使っていて、"mv: will not overwrite just-created" というメッセージに遭遇した。なんじゃこれ? 遭遇したのは、下記の状況。

  • 同じファイルが複数回のコピーを経て、複数のディレクトリに存在する( a/xxx.txt b/xxx.txt b/yyy.txt c/yyy.txt のように)
  • これらのファイルを1つのディレクトリに、重複無くまとめたい( 重複しているファイルは消したい)
  • ファイル名が同一なら中身も同一と仮定して良い

ここで、mv */*.txt new-dir/ と操作したら、上記メッセージに遭遇した。重複してるファイルは消えなかった。 mv -f しても状況は変わらないし、mv -uf するとメッセージはでなくなるが、(重複した)元ファイルは消えなかった。
たぶん、 mv -uf したあとに移動されていない元ファイルだけを rm すればいいんだろうけど、そもそもなんでこんなメッセージが出るんだろ? これは抑制できる動作だろうか?


manを見たり、上記メッセージにて検索してみたけど、ヒントになりそうな情報は無かった。仕方ないので、ソースを探す。GNU fileutilsだよな、と思ったら最近はcoreutilsに含まれているようだ。Fileutils, Shellutils, Textutils がcoreutilsに移動している。GNU Core Utilities Frequently Asked Questionsによると、2003年に移動したようだ。いまだにfileutilsなんて言ってると歳がばれるのか…

左の書籍も「Classic Shell Scripting」だし。昭和のころからのUNIX使ってたら、Classicにもなるか。たのしいUNIXで覚えた世代だし(^_^;


当該メッセージを表示してるのはsrc/copy.c内の下記の部分。copy.c は mv/cpで共通に使われてる。そういえば、昔は ln/mv/cp は同一バイナリで名前が違うだけだったような気がするけど… 少なくとも4.4BSD-Liteでは別バイナリだな。NET/2も別っぽい。いつの話だ、俺が覚えてるのは(^_^; ex と vi と view はいまどきのLinuxでも同一バイナリ。
(Index of V6/usr/source/s1でも別だ。あれ???)

minixのcp.1では、「cp, mv, rm, ln, cpdir, clone」は同一バイナリだから、これと記憶がごっちゃになってるのかな、俺。BBSてだこはMinuxで動かしてたからなあ…  SunOS4の /sbin はどうだったっけ?

さて、copy.c の該当部分は下記。私の遭遇した状況は、コメントに書かれているのと一緒だ。

              /* Don't let the user destroy their data, even if they try hard:
                 This mv command must fail (likewise for cp):
                   rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
                 Otherwise, the contents of b/f would be lost.
                 In the case of `cp', b/f would be lost if the user simulated
                 a move using cp and rm.
                 Note that it works fine if you use --backup=numbered.  */
              if (command_line_arg
                  && x->backup_type != numbered_backups
                  && seen_file (x->dest_info, dst_name, &dst_sb))
                {
                  error (0, 0,
                         _("will not overwrite just-created %s with %s"),
                         quote_n (0, dst_name), quote_n (1, src_name));
                  return false;
                }

seen_file (lib/file-set.c) ではハッシュを使って、同名ファイルか否かを確認している。 !! が面白いテクニック。通常Cでは偽は0、真は0以外だけど、真値を1に固定したい場合に二重否定を使う。 __builtin_expect と組み合わせた例がboolean – Double Negation in C++ code – Stack Overflowにある。

/* Return true if there is an entry in hash table, HT,
   for the file described by FILE and STATS.  */
bool
seen_file (Hash_table const *ht, char const *file,
           struct stat const *stats)
{
  struct F_triple new_ent;

  if (ht == NULL)
    return false;

  new_ent.name = (char *) file;
  new_ent.st_ino = stats->st_ino;
  new_ent.st_dev = stats->st_dev;

  return !!hash_lookup (ht, &new_ent);
}

と、いろいろ見たけど、どうもコマンドラインオプションなどでこの動作を抑制するのは不可能っぽい。あきらめて、移動できなかったファイルを削除する方向で考えよう。

そもそも昔のmvは、こういう確認はなかったような気がする (少なくともfileutils-3.13にはない。1996年)。root権限でmvやrmを使って痛い思いをしたことも幾度もある。そのころに比べるとUNIXのコマンドも便利に(そしておせっかいに)なったものだと思う。


リンク:

Linux

Posted by ず@沖縄