36

I have a lot of relative symbolic links that I want to move to another directory.

How can I move symbolic links (those with a relative path) while preserving the right path?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
anasdox
  • 629
  • 1
  • 7
  • 15

5 Answers5

42

You can turn relative paths into full paths using readlink -f foo. So you would do something like:

ln -s $(readlink -f $origlink) $newlink
rm $origlink

EDIT:

I noticed that you wish to keep the paths relative. In this case, after you move the link, you can use symlinks -c to convert the absolute paths back into relative paths.

Christopher Neylan
  • 8,018
  • 3
  • 38
  • 51
  • 5
    You could also use the `-r` option of `ln` to have it create a relative link, i.e. `ln -sr "$(readlink -f "$origlink")" "$newlink"; rm "$origlink"`. – Robin479 Apr 11 '18 at 11:32
  • Note: if the origlink does not exist, then it creates a garbage link. – ctrl-alt-delor Jun 04 '18 at 18:27
  • 1
    you should quote properly `ln -rs "$(readlink -f "$origlink")" "$newlink"` – ctrl-alt-delor Jun 04 '18 at 18:28
  • If you don't have the `symlinks` utility available, you can use the `-r` option of `ln` to convert an existing absolute symlink to a relative one. `ln -srf $newlink`. Or just do what @Robin479 suggests and just create a relative symlink in the first step. – Rachel Frei Jun 24 '20 at 17:59
  • On Mac OS `readlink` has no option `-f`, so you have to `brew install coreutils` and use `greadlink`, instead. – Martin Braun Jun 25 '21 at 22:31
9

This is a perl solution that preserves relative paths:

use strictures;
use File::Copy qw(mv);
use Getopt::Long qw(GetOptions);
use Path::Class qw(file);
use autodie qw(:all GetOptions mv);

my $target;
GetOptions('target-directory=s' => \$target);
die "$0 -t target_dir symlink1 symlink2 symlink3\n" unless $target && -d $target;

for (@ARGV) {
    unless (-l $_) {
        warn "$_ is not a symlink\n";
        next;
    }
    my $newlink = file(readlink $_)->relative($target)->stringify;
    unlink $_;
    symlink $newlink, $_;
    mv $_, $target;
}
Bernhard
  • 3,619
  • 1
  • 25
  • 50
daxim
  • 39,270
  • 4
  • 65
  • 132
3

Use ln of course:

for i in *; do # or whatever pattern you're wanting to match
    ln -sr "$(readlink "$i")" newdir/"$i";
done;

I was surprised this works, but LN(1) must be smart enough to take note of what's going on and help you out! I even tried it with a "newdir" of ../somethingelse (which should be a no-op in the link re-writing) and .. (which will remove a .. from the link target), and it worked wonderfully.

lmat - Reinstate Monica
  • 7,289
  • 6
  • 48
  • 62
0

Improving on Christopher Neylan's answer:

~/bin $ cat mv_ln
#!/bin/bash
#
# inspired by https://stackoverflow.com/questions/8523159/how-do-i-move-a-relative-symbolic-link#8523293
#          by Christopher Neylan

help() {
   echo 'usage: mv_ln src_ln dest_dir'
   echo '       mv_ln --help'
   echo
   echo '  Move the symbolic link src_ln into dest_dir while'
   echo '  keeping it relative'
   exit 1
}

[ "$1" == "--help" ] || [ ! -L "$1" ] || [ ! -d "$2" ] && help

set -e # exit on error

orig_link="$1"
orig_name=$( basename    "$orig_link" )
orig_dest=$( readlink -f "$orig_link" )
dest_dir="$2"

ln -r -s "$orig_dest" "$dest_dir/$orig_name"
rm "$orig_link"

This is also part of https://github.com/tpo/little_shell_scripts

0

One can use tar to move a folder containing relative symbolic links.

For example:

cd folder_to_move/..
tar czvf files.tgz folder_to_move
cd dest_folder/..
tar xzvf /absolute/path/to/folder_to_move/../files.tgz

# If all is fine, clean-up
rm /absolute/path/to/folder_to_move/../files.tgz
rm -rf /absolute/path/to/folder_to_move
ttq
  • 383
  • 3
  • 7
  • 1
    Could even use a tar pipe chain to do it all in one command ``cd folder_to_move; tar cf - . | (cd /usr/local/dest_folder; tar xf -)`` – Vorsprung Aug 31 '16 at 10:59
  • 4
    It doesn't seem to work for me. Exactly how and when are relative symlinks being rewritten by 'tar'? – Metamorphic Dec 01 '18 at 08:00
  • @Metamorphic See [here](https://man.freebsd.org/cgi/man.cgi?query=bsdtar&sektion=1&format=html). _Normally, symbolic links are archived as such._ This works for me in OSX and Linux. – ttq Jul 03 '23 at 14:09