在新的 SSD 上安装 Linux 时,我创建了一个newuser
与旧系统名称不同的用户olduser
。我成功地rsync
将所有文件从旧系统移动到了新 SSD 的主分区。但特别是在.steam
文件夹中,有大量符号链接(软链接)仍然使用/home/olduser
,而不是~
或$HOME
。我开始在 Bash 中重命名其中一些,但后来意识到它们太多了,比如 > 6000!
所以我的问题是:是否有一个 Bash/Python/Whatever 脚本可以
- 列出文件夹中的所有符号链接,包括子文件夹(
ls -lR /path/to/folder | grep '^l'
),以及 - 将所有
/home/olduser
符号/home/newuser
链接重命名
答案1
/
这可能有点棘手,因为从理论上讲,文件名可以包含除NUL 字节 ( )之外的任何内容\0
。这使得解析 的输出变得ls
很->
困难,因为您可以拥有一个名为 的文件file -> foo
:
$ touch 'file -> foo'
$ ls
'file -> foo'
另外,由于名称可以包含换行符,因此您建议的方法ls -l | grep '^l'
无法帮助您轻松收集名称:
$ ln -s 'file -> foo' 'some name'$'\n''with a newline'
$ ls -l
total 0
-rw-r--r-- 1 terdon terdon 0 Oct 15 12:36 'file -> foo'
lrwxrwxrwx 1 terdon terdon 11 Oct 15 12:37 'some name'$'\n''with a newline' -> 'file -> foo'
$ ls -l | grep '^l'
lrwxrwxrwx 1 terdon terdon 11 Oct 15 12:37 some name
如上所示,这只能捕获第一个换行符之前的文件名。所以我们需要更好的方法。幸运的是,find
有一个选项可以仅查找符号链接:
$ find . -type l
./some name?with a newline
有了这个,我们可以获取所有符号链接的列表,读取它们的目标并将其更改为更新的用户名:
find . -type l -print0 |
while IFS= read -r -d '' linkName; do
target=$(readlink -- "$linkName")
newTarget=$(sed 's|^/home/olduser/|/home/newuser/|' <<<"$target")
if [[ "$newTarget" != "$target" ]]; then
rm -- "$linkName" &&
ln -s -- "$newTarget" "$linkName"
fi
done
让我们详细分析一下:
find . -type l -print0
:这会查找当前目录中的所有链接,并将它们打印为 NUL\0
分隔列表。这让我们可以安全地处理奇怪的文件名,例如包含换行符的文件名。while IFS= read -r -d '' linkName; do ...; done
:我们将上一个命令的输出传递find
给此 while 循环,该循环读取以空字符分隔的 iunput (-d ''
) 并将IFS
变量设置为空字符串以避免在空格处拆分。确保在文件名中找到的-r
任何转义字符(例如\r
或\t
)都不会被视为转义字符,而是被视为简单字符串,以便我们可以处理\
名称中包含 的文件。循环本身会遍历 返回的每个链接名称find
,并将其保存为$linkName
。target=$(readlink "$linkName")
:这是链接指向的目标。请注意,我不是这里使用是-f
因为我们不想要最终目标,我们只想要第一级。这里其实没什么关系,因为如果目标目录不存在,你的所有链接都会被破坏,但这样更安全。newTarget=$(sed 's|^/home/olduser/|/home/newuser/|' <<<"$target")
:我们用sed
替换/home/olduser/
目标名称开头的 found ,/home/newuser/
并将结果路径保存为$newTarget
。if [[ "$newTarget" != "$target" ]]; then
:如果新的目标名称与旧的目标名称不同。rm -- "$linkName" && ln -s -- "$newTarget" "$linkName"
:删除现有链接,如果删除成功(&&
),则创建一个具有相同名称但指向新目标的新符号链接。
这应该可以安全地用于任意文件名。