How do I move a relative symbolic link?
Asked Answered
E

5

37

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?

Enamor answered 15/12, 2011 at 16:16 Comment(4)
Do you still want the links to be symbolic when moved? This has an influence on the answers you will get.Hopscotch
And also do you still want the links to be to relative paths, or is changing them to absolute paths ok?Clamatorial
I think this belongs on unix.stackexchange.com, though superuser.com would also work.Sob
@fgr yes, i want to keep relative symbolic links for example ../../dir/sym will be change to ../../../dir/symEnamor
M
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.

Maitland answered 15/12, 2011 at 16:24 Comment(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".Pipeline
Note: if the origlink does not exist, then it creates a garbage link.Multipurpose
you should quote properly ln -rs "$(readlink -f "$origlink")" "$newlink"Multipurpose
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 @Pipeline suggests and just create a relative symlink in the first step.Greenish
On Mac OS readlink has no option -f, so you have to brew install coreutils and use greadlink, instead.Neisse
H
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;
}
Hurlyburly answered 15/12, 2011 at 17:4 Comment(0)
B
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.

Bossy answered 26/5, 2021 at 19:6 Comment(0)
D
0

Improving on Christopher Neylan's answer:

~/bin $ cat mv_ln
#!/bin/bash
#
# inspired by https://mcmap.net/q/416287/-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

Demerit answered 28/5, 2015 at 14:3 Comment(4)
You forgot some quotes around the $(…)Multipurpose
@Multipurpose - could you please give an example where the missing quote for the $( ) would have a negative/unexpected impact?Geotaxis
In the same circumstances when missing them out from orig_link="$1". I just did some test, and I could not get it to break, may be I just put them in out of habit, because I find it easier to remember a simple rule, that avoids bugs, than a set of many rules.Multipurpose
No need for quotes when assigning to a variable. Not a bad habit, but not necessary.Bossy
I
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
Iorgo answered 31/8, 2016 at 10:55 Comment(4)
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 -)Olney
It doesn't seem to work for me. Exactly how and when are relative symlinks being rewritten by 'tar'?Dimerous
@Dimerous See here. Normally, symbolic links are archived as such. This works for me in OSX and Linux.Iorgo
This preserves the symlinks in the source folder "as-is" it doesn't re-write the links to accomodate any relative change to a new destination folder. So this will only work if the the src and dst folders happen to be siblings.Celeski

© 2022 - 2024 — McMap. All rights reserved.