0

Scenario: I'm writing a shell script that checks to see if a directory exists; if it does, and it's a symlink, it renames the directory with .bak appended to the directory name. I noticed, however, that if I'm moving a symlinked directory (directory A) to another symlinked directory (B), it causes A to be moved inside B, rather than overwriting B.

To illustrate:

$ mkdir foo                                                                     
$ mkdir bar                                                                     
$ ln -s "$PWD/foo" ~/.baz                                                       
$ ls -al ~/.baz                                                                 
lrwxr-xr-x  1 ysim  staff  15 Sep 11 21:45 /Users/ysim/.baz -> /Users/ysim/foo  
$ mv ~/.baz ~/.bam                                                              
$ ls -al ~/.bam                                                                 
lrwxr-xr-x  1 ysim  staff  15 Sep 11 21:45 /Users/ysim/.bam -> /Users/ysim/foo  
$ ls -al ~/.baz                                                                 
ls: /Users/ysim/.baz: No such file or directory                                                                                                   
$ ln -s "$PWD/bar" ~/.baz                                                       
$ ls -al ~/.baz                                                                 
lrwxr-xr-x  1 ysim  staff  15 Sep 11 21:47 /Users/ysim/.baz -> /Users/ysim/bar  
$ ls -al ~/.bam                                                                 
lrwxr-xr-x  1 ysim  staff  15 Sep 11 21:45 /Users/ysim/.bam -> /Users/ysim/foo  

Now, when I rename/move ~/.baz to ~/.bam:

$ mv ~/.baz ~/.bam                 

The target of ~/.baz (/Users/ysim/bar) doesn't get moved to the target of ~/.bam, as expected:

$ ls -al ~/.bam                                                                 
lrwxr-xr-x  1 ysim  staff  15 Sep 11 21:45 /Users/ysim/.bam -> /Users/ysim/foo 

Instead, the entire ~/.baz symlink has been moved inside the ~/.bam directory:

$ ls -al ~/.bam/
total 8
drwxr-xr-x   3 ysim  staff   102 Sep 11 21:47 .
drwxr-xr-x+ 96 ysim  staff  3264 Sep 11 21:51 ..
lrwxr-xr-x   1 ysim  staff    15 Sep 11 21:47 .baz -> /Users/ysim/bar

Why is that? And how do I go about doing what I set out to do, which is to write the target of ~/.baz to the target of ~/.bam, so that ~/.bam has the destination /Users/ysim/bar?

3cheesewheel
  • 9,133
  • 9
  • 39
  • 59

1 Answers1

1

It works like that because mv asks the OS to do the moving, and the OS dereferences symlinks. To behave differently mv would need extra code to check for symlinks and some --dereference-target, --dereference-src (or such) options. Think about the number of different behaviours different users might find desirable.

You need

rm "$dest"
cp -a "$src" "$dest"
# in your case src=".bam" and dest=".baz"

BTW if in a script you want to

  • "read" the target of a symlink in a script: target=$(readlink "$f")
  • test whether a file is a symlink: test -l "$f"
dan3
  • 2,528
  • 22
  • 20
  • Ah, I see. So, for example -- and correct me if I'm wrong -- it would be unclear whether you wanted to move the source of A to the source of B like I want to do (`~/.bam -> /Users/ysim/bar`), or the target of A to the target of B (`~/.baz -> /Users/ysim/foo`), or to have `~/.bam` go away completely (only `~/.baz -> ~/bar`). – 3cheesewheel Sep 12 '13 at 14:14
  • It would be unclear *if* `mv` decided to do anything beyond what the OS/libc provides. But `mv` *doesn't*, probably because like you said there would be many confusing options :) – dan3 Sep 12 '13 at 15:37