批量更新符号链接


12

我有一个Web应用程序,整个子目录中都有很多符号链接。我需要将应用程序移至另一个目录结构,并且需要更新所有符号链接以指向新路径。例如:

旧目录:/home/user/public_html/dev
新目录:/home/user/public_html/qa
旧符号链接:/home/user/public_html/qa/multisites/slave01/images -> /home/user/public_html/dev/images
新符号链接:/home/user/public_html/qa/multisites/slave01/images -> /home/user/public_html/qa/images

问题在于,其中许多分散在各个目录中。如何从根递归搜索并重新创建所有指向/dev/with的符号链接/qa/

Answers:


16

此bash命令应为您完成此操作:

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

它用于find标识目录中的所有文件,这些文件是与qa目录中目标的符号链接dev,并且针对每个文件,打印出一个bash命令,该命令将用指向的等效路径的链接替换该链接qa/。运行此命令后,只需执行生成的脚本即可

bash script.sh

您可能要先手动检查它,以确保它起作用。

这是上面find命令的更详细的版本,以方便阅读(尽管在实践中我不一定会这样写):

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

这将创建一个空的script.sh。并按以下方式运行find命令: find /home/user/public_html/qa/ -type l -lname '/home/user/public_html/dev/*' 不输出任何内容。
ggutenberg

您确实记得将路径更改为文件系统上的实际路径,对吗?如果您只跑步会find /home/usr/public_html/qa/ -type l怎样?如果找不到链接,则说明您的系统发生了很奇怪的事情。
David Z

是的,“ find /home/user/public_html/qa/ -type l”输出链接。但是添加-lname参数不会输出任何内容。
ggutenberg 2010年

实际上,在进一步测试中,这似乎是可行的。不知道昨天我在做什么错,但现在看来还可以。谢谢。
ggutenberg 2010年

嗯,很奇怪。好吧,如果您找出问题出在哪里,请在此处发表评论。我很好奇。
David Z

5

如果其他人在搜索解决方案时发现了此问题:创建一个名为“ linkmod.sh”的文件,其中包含:

#!/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"

并运行

find . -type l -print0 | xargs -0IX echo linkmod.sh X "pattern1" "pattern2"

如果需要,可以在查找中使用-lname选项。

注意:在sed中需要\的任何字符之前,必须在模式中使用2x \,因为echo会删除一个。例如

find . -type l -print0 | xargs -0IX echo linkmod.sh X "folder\\ name\\/file" "folder2\\ name\\/file"

echo如果ln命令正确,则从最后一行删除。


可能是很好澄清的是,echo在最后一行的脚本echofind .. | xargs .. linkmod.sh ...命令本身需要被移除。
凯尔·斯特兰德

2

link_rename.sh为给定目录中的递归重命名符号链接创建了一个bash脚本

#! /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)

可以执行为 link_rename.sh /home/human/dir link1 link2

该脚本有3个参数:

  1. 您要在其中执行符号链接的批量重命名的目录
  2. 旧模式。这link1是将被替换的旧模式
  3. 新模式。这link2link1将被替换的新模式

该脚本使用递归读取目录中的所有符号链接,find "$DIR" -type l 并逐行处理它。

$line 是需要重命名的符号链接

CUR_LINK_PATH 是老路

NEW_LINK_PATH 通过在旧链接路径中执行字符串替换获得。

删除旧的符号链接,并使用创建新的符号链接 ln -s "$NEW_LINK_PATH" "$line"


0

我最终写了一个命令行PHP脚本,该脚本似乎可以解决问题。

<?php
//Run via command-line
$dir = new RecursiveDirectoryIterator('.');
foreach(new RecursiveIteratorIterator($dir) as $file) {
    //$link = readlink($file);
    if(is_link($file)) {
        $old_link = readlink($file);
        $new_link = str_ireplace("/joomla/", "/qa/", $old_link);
        if($new_link != $old_link) {
            exec('rm "'.$file.'"');
            exec('ln -fs "'.$new_link.'" "'.$file.'"');
            $new_link = readlink($file);
            if($new_link == $old_link) {
                echo $file."\t".$old_link."\t".$new_link."\n";
            }
        }
    }
}
?>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.