给出以下人为的示例:
$ 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
一次循环遍历所有目录**
。之后剩下的就是玩一点readlink
、realpath
等:
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
写入.我提供的正则表达式参数最常用于更改流中的文件名 - 正如我在这里使用第二个一样- 但通过一两个技巧,它也可以用于过滤添加到存档中的文件。ustar
stdout
-s
pax
您会看到,任何在应用该 arg 后变为空的文件名都-s
被指定为从存档中完全删除。因此,我使用一个正则表达式来匹配每个文件名,但也会删除每个文件中与保存在\1
.这是必要的,因为虽然您可以根据模式轻松选择存档中的文件,但-w
编写存档时情况并非如此 - 这取决于 shell 全局变量,因此不会递归。
尽管如此,所编写的正则表达式仍然会跟随任何字符,因此,在另一端,我在列出存档时在选择中实现的/original-target
模式之一是- 它将把它放在那里。即使这并不完美 - 我的意思是,也许您拥有的链接包含两次该模式,但是......好吧,它就是这样 - 而且非常好。可以通过同样的方式进行进一步测试来处理它。不管怎样,我还实现了一个与自身的匹配- 因此它也从列表输出中删除。-c
*/original-target?*
-c
././a/b/original-file
在列表模式下 - 这是pax
当它没有-r
ead 或-w
rite 时的操作 -pax
列表冗长地-ls -l
格式 - 包括任何软链接目标,例如link => target
- 但仅在应用任何适用的-s
参数之后。-s
无论何时以以下格式应用修饰符,都会打印-v
这些完全相同-s|...|...|p
的参数:p
"%s>>%s\n",原始路径名,新路径名
././
因此,上面我们将列表与否则不会出现的列表分开。现在这让事情变得非常简单,我希望你会同意。我将让读者决定如何应用此类数据ln
- 它应该相当简单 - 但是你甚至不需要这样做。
事实上,它的规格是允许您使用 cli 选项在ead 或rite 时pax
更改存档,以更改用户、组、链接数据、文件名、文件源等内容。这些是非常强大的文件解析工具 - 但是我所知道的最接近实现它们的是 GNU ,而且只是部分实现。我还没有找到任何影响头文件类型的方法 - 尽管你应该能够在某种程度上做到这一点 - 使用 cli 选项,即使在存档本身上使用正则表达式并不是非常困难。这个东西有一个头字段:r
w
pax
tar
... 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
这样做的......好吧,你知道谁在寻找。