linuxbashln

How change symlink path for many files?


I was changed directory name. In this directory thousands of files. Some projects use this files, projects have got symlinks on it.

  1. How to find all symlinks, which have got folder name in their address?
  2. how to change all this symlinks to another path in automatic mode?

if 2 only bash scripting with deleting and creating new - i will do it, but may be you know more easy way?


Solution

    1. It's a bit complicated, but it can be done with find, readlink, a check to test whether the symlink is relative or not, and sed to get rid of .. in path names (copied 1:1 from this answer).
      (Note that most convenient methods (such as readlink -f) are not available due to the symlinks targets not existing anymore.)
      Assuming your old path is /var/lib/old/path:

      oldpath='/var/lib/old/path';
      find / -type l -execdir bash -c 'p="$(readlink "{}")"; if [ "${p:0:1}" != "/" ]; then p="$(echo "$(pwd)/$p" | sed -e "s|/\./|/|g" -e ":a" -e "s|/[^/]*/\.\./|/|" -e "t a")"; fi; if [ "${p:0:'${#oldpath}'}" == "'"$oldpath"'" ]; then ...; fi;' \;
      
    2. Now replace the ... from above with ln -sf (-f to override the existing link).
      Assuming your new path is /usr/local/my/awesome/new/path:

      oldpath='/var/lib/old/path';
      newpath='/usr/local/my/awesome/new/path';
      find / -type l -execdir bash -c 'p="$(readlink "{}")"; if [ "${p:0:1}" != "/" ]; then p="$(echo "$(pwd)/$p" | sed -e "s|/\./|/|g" -e ":a" -e "s|/[^/]*/\.\./|/|" -e "t a")"; fi; if [ "${p:0:'${#oldpath}'}" == "'"$oldpath"'" ]; then ln -sf "'"$newpath"'${p:'${#oldpath}'}" "{}"; fi;' \;
      

    Note that oldpath and newpath have to be absolute paths.
    Also note that this will convert all relative symlinks to absolute ones.
    It would be possible to keep them relative, but only with a lot of effort.

    Breaking it down

    For those of you who care what that one-line-inferno actually means: