如何更改许多文件的符号链接路径?

How change symlink path for many files?

我被更改了目录名。 在这个目录中有数千个文件。 一些项目使用这个文件,项目有符号链接。

  1. 如何找到地址中包含文件夹名称的所有符号链接?
  2. 如何在自动模式下将所有这些符号链接更改为另一个路径?

如果只有 2 个 bash 脚本删除和创建新的 - 我会做,但你知道更简单的方法吗?

  1. 有点复杂,但是可以用findreadlink,检查symlink是否是相对的,和 sed 去除路径名中的 ..(从 this answer 复制 1:1)。
    (请注意,由于 symlinks 目标不再存在,因此最方便的方法(例如 readlink -f)不可用。)
    假设您的旧路径是 /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. 现在用 ln -sf 替换上面的 ...-f 覆盖现有的 link)。
    假设您的新路径是 /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;' \;
    

注意 oldpathnewpath 必须是绝对路径。
另请注意,这会将所有相对符号 link 转换为绝对符号。
保持它们的相对性是可能的,但需要付出很多努力。

分解

对于那些关心一行地狱的实际含义的人:

  • find - 一个很棒的可执行文件
  • / - 搜索位置,在本例中为系统根目录
  • -type l - 匹配符号 links
  • -execdir - 对于每个匹配项 运行 在匹配文件的目录中执行以下命令:
    • bash - 嗯,bash
    • -c - 执行以下字符串(前导和尾随 ' 已删除):
      • p="$(readlink "{}")"; - 从最内层开始:
        • " - 开始一个字符串以确保不会发生扩展
        • {} - 匹配文件名的占位符(-execdir 的特征)
        • " - 结束字符串
        • readlink ... - 找出 symlink 指向
        • 的位置
        • p="$(...)" - 并将结果存储在 $p
      • if [ "${p:0:1}" != "/" ]; then - 如果 $p 的第一个字符是 /(即 symlink 是绝对的),那么...
      • p="$(echo "$(pwd)/$p" | sed -e "s|/\./|/|g" -e ":a" -e "s|/[^/]*/\.\./|/|" -e "t a")"; - 将路径转换为绝对路径:
        • $(pwd) - 当前目录(匹配文件所在的目录,因为我们使用的是 -execdir
        • /$p - 在工作目录
        • 的路径中附加一个斜线和 symlink 的目标
        • echo "$(pwd)/$p" | - 将上面的内容通过管道传递给下一个命令
        • sed ... - 解析所有 ..,参见 here
        • p="$(...)" 并将结果存储回 $p.
      • fi; - 结束if
      • if [ "${p:0:'${#oldpath}'}" == "'"$oldpath"'" ]; - 如果 $p$oldpath 开头
        • ${p:0:'${#oldpath}'} - $p 的子字符串,从位置 0 开始,长度为 $oldpath
          • ${#oldpath} - 变量的长度 $oldpath
          • '...' - 必需,因为我们在 ' 引号内
      • then - 然后...
      • ln -sf - link 象征性地覆盖现有文件,参数:
        • "'"$newpath"'${p:'${#oldpath}'}" - 用 $newpath 替换 $p$oldpath 部分(实际上从 $p 中删除与 $oldpath 一样多的字符是,并在其前面添加 $newpath):
          • " - 开始一个字符串
          • ' - 将 ' 字符串参数结束为 bash -c
          • " - 向其附加一个 " 字符串(其中发生变量扩展),包含:
          • $newpath - $newpath
          • 的值
          • " - 将 " 字符串参数结束为 bash -c
          • ' - 向其附加一个 ' 字符串,包含:
          • ${p: - p 的子字符串,起始于:
          • ' - 结束对 bash -c
          • 的论证
          • ${#oldpath} - 将 $oldpath 的长度附加到它
          • ' - 向其附加另一个 '-字符串
          • } - 结束子字符串
          • " - 结束字符串
        • "{}" - link 文件,其路径保持不变
      • fi; - 结束if
  • \; - -execdir
  • 的分隔符