用于替换目标的所有符号链接以指向另一个目标(在同一目录中)的脚本

用于替换目标的所有符号链接以指向另一个目标(在同一目录中)的脚本

给出以下人为的示例:

$ mkdir -p a/b && touch a/b/original-target && touch a/b/new-target
$ mkdir -p c1/c2 && cd c1/c2 && ln -s ../../a/b/original-target && cd -
$ mkdir -p d1/d2/d3 && cd d1/d2/d3 && ln -s ../../../a/b/original-target && cd -
$ tree .
.
├── a
│   └── b
│       ├── new-target
│       └── original-target
├── c1
│   └── c2
│       └── original-target -> ../../a/b/original-target
└── d1
    └── d2
        └── d3
            └── original-target -> ../../../a/b/original-target

如何将所有符号链接替换为original-target符号链接到new-target.您可以假设原始目标和新目标都位于同一目录中(如上面设计的示例中的情况)。符号链接必须使用相对路径。

答案1

我假设您使用的是 bash,所以我会使用 .globstar一次循环遍历所有目录**。之后剩下的就是玩一点readlinkrealpath等:

shopt -s globstar
for file in c1/** d1/**; do
    if [[ -h "$file" ]]; then
        if [[ "$(readlink -f "$file")" == "$(realpath a/b/original-target)" ]]; then
            ln -sf "$(dirname "$(readlink "$file")")"/new-target "$(dirname "$file")" && rm -f "$file"
        fi
    fi
done

答案2

pax真的在这些情况下很有用。事实上,如果我能找到一个pax可以做到这一点的-o listopt=...选项,那就更容易了由 POSIX 指定但是,尽管我四处寻找,但我还没有找到一个能做到这一点的人。我使用 mirabilos 维护的一种 - BSDpax mirabipax?)- 据我所知,这可能是大多数其他人所做的。不管怎样,你可以得到正则表达式文件名和文件列表。例如:

(set -e; mkdir -p a/b c1/c2 d1/d2/d3
touch a/b/original-target a/b/new-target
cd c1/c2; ln -s ../../a/b/original-target
cd ../../d1/d2/d3; ln -s ../../../a/b/original-target)

这就是你的树。现在我就列出来:

pax -ws'|\(\..*/original-target.*\)*.*|\1|' ././ | 
pax -cvs'|\(.*\)/original|\1/new|p' \
     '././a/b/original-target' '*/original-target?*'

哪个打印...

././d1/d2/d3/original-target >> ././d1/d2/d3/new-target
lrwxrwxrwx  1 mikeserv mikeserv         0 Dec  7 18:17 ././d1/d2/d3/new-target => ../../../a/b/new-target
././c1/c2/original-target >> ././c1/c2/new-target
lrwxrwxrwx  1 mikeserv mikeserv         0 Dec  7 18:14 ././c1/c2/new-target => ../../a/b/new-target

好的,所以第一个将存档pax写入.我提供的正则表达式参数最常用于更改流中的文件名 - 正如我在这里使用第二个一样- 但通过一两个技巧,它也可以用于过滤添加到存档中的文件。ustarstdout-spax

您会看到,任何在应用该 arg 后变为空的文件名都-s被指定为从存档中完全删除。因此,我使用一个正则表达式来匹配每个文件名,但也会删除每个文件中与保存在\1.这是必要的,因为虽然您可以根据模式轻松选择存档中的文件,但-w编写存档时情况并非如此 - 这取决于 shell 全局变量,因此不会递归。

尽管如此,所编写的正则表达式仍然会跟随任何字符,因此,在另一端,我在列出存档时在选择中实现的/original-target模式之一是- 它将把它放在那里。即使这并不完美 - 我的意思是,也许您拥有的链接包含两次该模式,但是......好吧,它就是这样 - 而且非常好。可以通过同样的方式进行进一步测试来处理它。不管怎样,我还实现了一个与自身的匹配- 因此它也从列表输出中删除。-c*/original-target?*-c././a/b/original-file

在列表模式下 - 这是pax当它没有-read 或-write 时的操作 -pax列表冗长地-ls -l格式 - 包括任何软链接目标,例如link => target- 但仅在应用任何适用的-s参数之后。-s无论何时以以下格式应用修饰符,都会打印-v这些完全相同-s|...|...|p的参数:p

"%s>>%s\n",原始路径名,新路径名

././因此,上面我们将列表与否则不会出现的列表分开。现在这让事情变得非常简单,我希望你会同意。我将让读者决定如何应用此类数据ln- 它应该相当简单 - 但是你甚至不需要这样做

事实上,它的规格是允许您使用 cli 选项在ead 或rite 时pax更改存档,以更改用户、组、链接数据、文件名、文件源等内容。这些是非常强大的文件解析工具 - 但是我所知道的最接近实现它们的是 GNU ,而且只是部分实现。我还没有找到任何影响头文件类型的方法 - 尽管你应该能够在某种程度上做到这一点 - 使用 cli 选项,即使在存档本身上使用正则表达式并不是非常困难。这个东西有一个头字段:rwpaxtar

... 2 - 代表符号链接。符号链接的内容应存储在链接名场地。

我真正想做的另一件事pax-o listopt=...。以下是规范示例部分的片段:

使用选项:

-o listopt="%M %(atime)T %(size)D %(name)s"

...覆盖标准输出中的默认输出描述,而是写入:

-rw-rw--- Jan 12 15:53 2003 1492 /usr/foo/bar

使用选项:

-o listopt='%L\t%(size)D\n%.7' \
-o listopt='(name)s\n%(atime)T\n%T'

...覆盖标准输出中的默认输出描述,而是写入:

/usr/foo/bar -> /tmp   1492
/usr/fo
Jan 12 15:53 1991
Jan 31 15:53 2003

所以,如果你偶然发现一个pax这样做的......好吧,你知道谁在寻找。

相关内容