Batch Update Symbolic Links Recursively
I have a web app that has a bunch of symbolic links in subdirectories throughout it. I need to move the app to another directory structure, and I need to update all the symlinks to point to the new path. For example:
Old Dir: /home/user/public_html/dev
New Dir: /home/user/public_html/qa
Old Symlink: /home/user/public_html/qa/multisites/slave01/images -> /home/user/public_html/dev/images
New Symlink: /home/user/public_html/qa/multisites/slave01/images -> /home/user/public_html/qa/images
The problem is that there's a lot of these scattered throughout various directories. How can I recursively search from the root and recreate all symlinks pointing to /dev/
with /qa/
?
This bash command should do it for you:
find /home/user/public_html/qa/ -type l -lname '/home/user/public_html/dev/*' -printf 'ln -nsf "$(readlink "%p" | sed s/dev/qa/)" "$(echo "%p" | sed s/dev/qa/)"\n' > script.sh
It uses find
to identify all files in the qa
directory that are symbolic links with a target that's in the dev
directory, and for each one, it prints out a bash command that will replace the link with a link to the equivalent path in qa/
. After you run this, just execute the generated script with
bash script.sh
You might want to examine it manually first to make sure it worked.
Here's a more verbose version of the above find
command for easier reading (though I wouldn't necessarily write it this way in practice):
SRC_DIR="/home/user/public_html/qa"
OLD_TARGET="/home/user/public_html/dev"
SUB="s/dev/qa/"
find $SRC_DIR -type l \
-lname "$OLD_TARGET/*" -printf \
'ln -nsf "$(readlink "%p"|sed $SUB)" "$(echo "%p"|sed $SUB)"\n'\
> script.sh
In case anyone else finds this when searching for a solution: Create a file named "linkmod.sh" containing:
#!/bin/sh
PATTERN1=`echo "$2"`
PATTERN2=`echo "$3"`
LINKNAME=`echo "$1"`
OLDTARGET=`readlink "$1"`
NEWTARGET=`echo "$OLDTARGET" \
| sed -e 's/'"$PATTERN1"'/'"$PATTERN2"'/'`
echo ln -nsf "$NEWTARGET" "$LINKNAME"
and run
find . -type l -print0 | xargs -0IX echo linkmod.sh X "pattern1" "pattern2"
You can ofc use the -lname option in find if needed.
NOTE: you have to use 2x \ in the patterns before any characters that require \ in sed, since echo removes one. For example
find . -type l -print0 | xargs -0IX echo linkmod.sh X "folder\\ name\\/file" "folder2\\ name\\/file"
Remove the echo
from the last line if the ln commands are correct.
I created a bash script link_rename.sh
for the recursively renaming symbolic links in a given directory
#! /bin/bash
DIR=$1
OLD_PATTERN=$2
NEW_PATTERN=$3
while read -r line
do
echo $line
CUR_LINK_PATH="$(readlink "$line")"
NEW_LINK_PATH="$CUR_LINK_PATH"
NEW_LINK_PATH="${NEW_LINK_PATH/"$OLD_PATTERN"/"$NEW_PATTERN"}"
rm "$line"
ln -s "$NEW_LINK_PATH" "$line"
done <<< $(find "$DIR" -type l)
It can be executed as link_rename.sh /home/human/dir link1 link2
The script has 3 arguments:
- The directory in which you want to perform the batch rename of symlinks
- The old pattern. Here
link1
is the old pattern which will be replaced - The new pattern. Here
link2
is the new pattern with whichlink1
will be replaced
The script recursively reads all symlinks in the directory using find "$DIR" -type l
and processes it line by line.
$line
is the symlink which needs to be renamed
CUR_LINK_PATH
is the old path
NEW_LINK_PATH
is obtained by performing string replacement in the old link path.
The old symlink is removed and new symlink is created using ln -s "$NEW_LINK_PATH" "$line"