I'm not sure if this is possible. I've been fiddling with a MWE for awhile.
I'm using rsync in relative mode (-R) to copy a remote directory from a server onto my local machine. The remote directory may have symlinks. Sometimes the symlinks point within the scope of the relative directory, and sometimes they point outside of it.
When the links point inside the relative directory, I want to only copy the links (because the files are already synced, so those links should resolve).
When the links point outside the relative directory, I want to copy the files themselves because otherwise the links would likely not resolve.
I've setup a MWE to test various ways of doing this:
TEST_BASE=rsync_test
REMOTE_DPATH=$HOME/tmp/rsync-test/remote
LOCAL_DPATH=$HOME/tmp/rsync-test/local
REMOTE_URI=$REMOTE_DPATH
REMOTE_MOUNT=$REMOTE_DPATH
reset_rsync_test_remote()
{
# Clean
if [ -d "$REMOTE_DPATH" ]; then
rm -rf $REMOTE_DPATH
fi
mkdir -p $REMOTE_DPATH
# Setup remote data
mkdir -p $REMOTE_DPATH/$TEST_BASE/root/dir_L0_X0_A
mkdir -p $REMOTE_DPATH/$TEST_BASE/root/dir_L0_X0_A/dir_L1_X0_B
mkdir -p $REMOTE_DPATH/$TEST_BASE/root/dir_L0_X1_C
mkdir -p $REMOTE_DPATH/$TEST_BASE/root/inside_dir
mkdir -p $REMOTE_DPATH/$TEST_BASE/root/links
mkdir -p $REMOTE_DPATH/$TEST_BASE/outside_dir/
touch $REMOTE_DPATH/$TEST_BASE/root/file_L0_X0_a.txt
touch $REMOTE_DPATH/$TEST_BASE/root/dir_L0_X0_A/file_L1_X0_b.txt
touch $REMOTE_DPATH/$TEST_BASE/root/dir_L0_X1_C/file_L1_X0_c.txt
touch $REMOTE_DPATH/$TEST_BASE/root/inside_dir/inside_file.txt
touch $REMOTE_DPATH/$TEST_BASE/outside_dir/outside_file.txt
# Create links to inside and outside the sync root
ln -s $REMOTE_DPATH/$TEST_BASE/root/inside_dir/inside_file.txt $REMOTE_DPATH/$TEST_BASE/root/links/inside_flink.txt
ln -s $REMOTE_DPATH/$TEST_BASE/outside_dir/outside_file.txt $REMOTE_DPATH/$TEST_BASE/root/links/outside_flink.txt
ln -s $REMOTE_DPATH/$TEST_BASE/outside_dir $REMOTE_DPATH/$TEST_BASE/root/links/outside_dlink
ln -s $REMOTE_DPATH/$TEST_BASE/root/inside_dir $REMOTE_DPATH/$TEST_BASE/root/links/inside_dlink
ln -sr $REMOTE_DPATH/$TEST_BASE/root/inside_dir/inside_file.txt $REMOTE_DPATH/$TEST_BASE/root/links/rel_inside_flink.txt
ln -sr $REMOTE_DPATH/$TEST_BASE/outside_dir/outside_file.txt $REMOTE_DPATH/$TEST_BASE/root/links/rel_outside_flink.txt
ln -sr $REMOTE_DPATH/$TEST_BASE/outside_dir $REMOTE_DPATH/$TEST_BASE/root/links/rel_outside_dlink
ln -sr $REMOTE_DPATH/$TEST_BASE/root/inside_dir $REMOTE_DPATH/$TEST_BASE/root/links/rel_inside_dlink
tree $REMOTE_DPATH/
}
reset_rsync_test_local(){
# Setup home data
echo "LOCAL_DPATH = $LOCAL_DPATH"
if [ -d "$LOCAL_DPATH" ]; then
rm -rf $LOCAL_DPATH
fi
mkdir -p $LOCAL_DPATH
mkdir -p $LOCAL_DPATH/$TEST_BASE
# Make an existing link on the destination that we will sync to
mkdir -p $LOCAL_DPATH/link-dest1
mkdir -p $LOCAL_DPATH/link-dest2
ln -s $LOCAL_DPATH/link-dest1 $LOCAL_DPATH/rsync_test-link
ln -s $LOCAL_DPATH/link-dest2 $LOCAL_DPATH/rsync_test-link/root
tree $LOCAL_DPATH
}
reset_rsync_test_remote
This will setup my fake "remote" directory, which looks like this:
The relative directory that we are going to sync is "root".
Notice the links on the bottom. Some of them point inside "root" (in which case they are have inside_ in their name) and some point outside of "root", (in which case they have outside_ in their name). Then half of them are absolute links and the other half are relative links (rel_ prefix). This enumerates all 8 possibilities I'm concerned with here.
I'm going to rsync the "root" directory to an existing symlink within a symlink on my "local" machine to simulate my normal use case. This shouldn't matter too much, it just means we have to specify (-K) when running rsync.
So far I have two methods for doing roughly what I want to do but each has flaws. In the first method I use (-L), which simply resolves all links to their hard files.
# Method 1 with -KL
# The -K is important when syncing to a destination dir that is a symlink
# The -L will resolve any symlinks
# this grabs everything however, all files will be copied over as hard files
reset_rsync_test_local
rsync -avPRKL $REMOTE_URI/$TEST_BASE/./root $LOCAL_DPATH/rsync_test-link
ls -al $LOCAL_DPATH/
ls -al $LOCAL_DPATH/rsync_test-link/
tree $LOCAL_DPATH/link-dest2
Method 2 gets closer, in that it at least links the rel_inside_flink but doesn't link the rel_inside_dlink and it fails to resolve the rel_outside_dlink.
# Method 2: with -Kk --copy-unsafe-links
# Alternatively using -k --copy-unsafe-links will get almost everything
# links inside the relative directory are copied as links, links outside
# the relative dir are copied as files, except relative outside files for
# whatever reason.
reset_rsync_test_local
rsync -avPRKk --copy-unsafe-links $REMOTE_URI/$TEST_BASE/./root $LOCAL_DPATH/rsync_test-link
ls -al $LOCAL_DPATH/
ls -al $LOCAL_DPATH/rsync_test-link/
tree $LOCAL_DPATH/link-dest2
What I'd like is information on how to accomplish either of the following:
In the better than what I have case:
What I'd like is for any rel_inside link would be copied directly as a link, while all other links were resolved and copied as files.
In the absolutely ideal case: it would convert the absolute links that point inside the relative directory to either relative or absolute links at the destination (e.g. inside_dlink would either convert itself to ../inside_dir or /home/joncrall/tmp/rsync-test/local/rsync_test/root/inside_dir).
e.g. from the Mac man pages:
man ln
-h If the NewLinkFile (or directory) is a symbolic link, do not follow
it. This is most useful with the -f option, to replace a symlink
which can point to a directory.
I am writing a script which once a day iterates over hundreds of files and symlinks them all and then periodically through out the day checks for new files and symlinks them. Would be great to use this -h check or something similar as at the moment it is done very awkwardly.
A GNU equivalent of the BSD ln -sh is ln -sn.
But GNU's ln -sT is safer, because it will skip both symbolic links and directories.
Compare:
cd "$(mktemp -d)"
mkdir bin
for d in bin usr etc; do ln -sn "/$d" "$d"; done
# will create a bin/bin -> /bin symlink
vs.
cd "$(mktemp -d)"
mkdir bin
for d in bin usr etc; do ln -sT "/$d" "$d"; done
ln: failed to create symbolic link 'bin': File exists
I want to symlink all the files which start with "sun" in the dir /myTest/logs/ to /finalProject/logs/sun
i tried using ln -sd /finalProject/logs/sun /myTest/logs/*
but i get error saying target is not a dir.
can somebody help.
You can't symlink multiple files with a single command. But a little bash for loop will do what you need:
for i in /finalProject/logs/sun*
do
ln -s $i /myTest/logs/
done
I have a directory with several "dot files". I would like to symlink all these files.
I tried
$ ln -s /usr/dotfiles/* /usr/test
ln: creating symbolic link `/usr/test/*' to `/usr/dotfiles/*': No such file or directory
ln -s /usr/dotfiles/.[!.]* /usr/test
Inspired from
pavium’s answer
This question already has answers here:
Can you change what a symlink points to after it is created?
(8 answers)
Closed 5 years ago.
So I created a symlink:
ln -s /location/to/link linkname
Now I want to change the location that the symlink links to. How do I do that? is there a way to do it without deleting it first?
You could create the new link with a different name, then move it to replace the old link.
ln -s /location/to/link linkname
Later
ln -s /location/to/link2 newlink
mv newlink linkname
If newlink and linkname are on the same physical device the mv should be atomic.
Try ln -sf new_destination linkname.
Just change the symlink target:
# ln -sfT /path/to/new/target linkname
This is an instant, atomic change.
If the symlink targets are directories, you need to add the -T flag to the mv command, otherwise it moves the new symlink in to the target directory of the old symlink.
Example of atomically switching a website to a new version:
Original setup - website is stored in www1 directory, vhost pointing at www symlink:
ln -s www1 www
Browse to website, see old version.
Put new website files in new www2 directory.
Set up new symlink to new website:
ln -s www_new www2
Move www symlink to directory of new website:
mv -T www_new www
Browse to website, see new version immediately.
On OSX, the man page for ln says you can do it like this
ln -shf /location/to/link link name
From the man page:
The options are as follows:
-F If the target file already exists and is a directory, then remove it so that the link may occur. The -F
option should be used with either -f or -i options. If none is specified, -f is implied. The -F option is
a no-op unless -s option is specified.
-h If the target_file or target_dir is a symbolic link, do not follow it. This is most useful with the -f
option, to replace a symlink which may point to a directory.
-f If the target file already exists, then unlink it so that the link may occur. (The -f option overrides any
previous -i options.)
-i Cause ln to write a prompt to standard error if the target file exists. If the response from the standard
input begins with the character `y' or `Y', then unlink the target file so that the link may occur. Other-
wise, do not attempt the link. (The -i option overrides any previous -f options.)
-n Same as -h, for compatibility with other ln implementations.
-s Create a symbolic link.
-v Cause ln to be verbose, showing files as they are processed.
For directories, you want to do:
ln -sfT /location/to/new/target old_linkname
No. The symlink system call will return EEXIST if newpath already exists. You can only link from a new node in the filesystem. What's the requirement here? If you're worried about a race due to the non-atomicity of the unlink/symlink calls, then you might want to rethink the architecture a little to provide synchronization elsewhere. There have been some scary security bugs introduced by this kind of thing.
As others have mentioned, you basically have to delete the symlink first, either manually or by passing the -f flag to the ln utility.
Years ago, I had to make small edits to symlinks pretty frequently, so I wrote a simple readline-based utility (edln) to make this less annoying. In case anyone else finds it useful, I've put it online at https://github.com/jjlin/edln/.
edln will display the original symlink target; you can then use the arrow keys, or standard readline keystrokes (M-b, M-f, C-d, etc.) to move around and edit the target.
Chain the commands like this:
rm currentlink && ln -s /path/to/link currentlink
The first command removes the existing one and the 2nd immediately creates it again.
Just googled, found no good answer and had to solve myself:
ln -f -s -T `readlink SomeLibrary | sed 's/version.old/version.new/'` SomeLibrary
Editing by definition means not recreating from scratch but changing partly. Any answer requiring to memorize a path, maybe long or with weird symbols, is definitely bad.