在脚本中,`ln -s` 的作用类似于 `cp`。

5

问题

我有这个bash脚本:

ACTIVE_DB=$(grep -P "^[ \t]*db.active" config.properties | cut -d= -f2 | tr -s " ")
echo $ACTIVE_DB
if [ "$ACTIVE_DB" = "A" ]
then
    ln -sf config-b.properties config.properties
else
    ln -sf config-a.properties config.properties
fi

config-a.properties

db.active = A

config-b.properties

db.active = B

当我运行脚本时,会执行一次硬拷贝(=cp),而config.properties通常不是符号链接(也不是物理链接),而是一个全新的文件,其内容与config-a.propertiesconfig-b.properties相同。
$ ls -li
53 -rw-r--r-- 1 ogregoir ogregoir     582 Sep 30 15:41 config-a.properties
54 -rw-r--r-- 1 ogregoir ogregoir     582 Sep 30 15:41 config-b.properties
56 -rw-r--r-- 1 ogregoir ogregoir     582 Oct  2 11:28 config.properties

当我手动逐行在提示符中运行此命令时,没有任何问题,并且符号链接确实始终会被创建,config.properties 指向 config-a.properties 或者 config-b.properties
$ ls -li
53 -rw-r--r-- 1 ogregoir ogregoir     582 Sep 30 15:41 config-a.properties
54 -rw-r--r-- 1 ogregoir ogregoir     582 Sep 30 15:41 config-b.properties
55 lrwxrwxrwx 1 ogregoir ogregoir      20 Oct  2 11:41 config.properties -> config-b.properties

注意事项

  • 没有任何其他地方打开文件(我是唯一的活动用户,使用配置文件的应用程序也未在运行)。
  • 有时ln -sf会表现正常,但通常规则是它会制作一个硬链接副本。
  • 脚本是从另一个目录运行的,但在执行此处操作之前,会cd到包含config*.properties文件的目录。
  • 脚本要长得多,但这是重现错误的最短示例。
  • bash版本为4.1.2(它是本地的,所以我不关心shellshock)。
  • ln版本为8.4。
  • 操作系统:Red Hat Enterprise Linux Server release 6.5(Santiago)。
  • 用于该文件夹的文件系统:ext4。

问题

  • 为什么我的脚本不一致地创建符号链接而是制作硬链接副本?
  • 如何在此处强制进行符号链接?

6
“ln” 命令不会创建副本。 - hek2mgl
1
是的,我可以阅读“man ln”,但它仍然会随机出错! - Olivier Grégoire
1
在每次调用之前添加 command -v ln,以显示 ln 实际调用的内容(以排除 shell 函数或意外二进制文件,如 /this/is/wrong/ln)。由于符号链接的目标甚至不需要存在,因此先前的命令对您观察到的内容几乎没有任何影响。 - chepner
4
我怀疑您有其他的脚本或代码正在覆盖符号链接。例如,“sed -i”会破坏符号链接。有许多命令和工具通过创建一个副本,修改该副本,然后将该副本移动到原始文件上来修改文件内容,这样会破坏原始的符号链接。或者另一种解释是:您没有运行您认为的脚本,或者没有修改您认为的文件。 - John Kugelman
1
@JohnKugelman 哦,不错!我确实后来在 config.properties 中使用了 sed -i。等我回到工作岗位再检查一下。 - Olivier Grégoire
显示剩余10条评论
2个回答

3

我怀疑您有其他脚本或代码正在覆盖符号链接。例如,sed -i 会将符号链接搞乱。有各种命令和实用程序通过创建副本、修改副本,然后将副本移动到原始位置来修改文件,这会破坏原始的符号链接。


确实是这样。我尝试了使用和不使用 sed -i 命令,结果不同。非常感谢!我没有想到它会替换文件,所以我在我的小测试中留下了它,但没有复制/粘贴到问题中。抱歉。 - Olivier Grégoire
我现在使用 sed --follow-symlinks -i,我的脚本正按照我的意愿执行。谢谢! - Olivier Grégoire
这个命令:sed -i "$(realpath ./config.properties)" 也可以通过(完全)解析文件来工作。参考链接:https://dev59.com/5nVD5IYBdhLWcg3wWaNh - user2350426

1
作为所提出的问题的唯一可能答案:为什么ln的行为类似于cp,是不可能的。
唯一可能的另一个答案是:您呈现给我们的不完全是正在执行的内容,或者有其他脚本在运行并改变了答案。
一些可能的替代方案: 1.- ln命令实际上正在执行硬链接。i-node列表(ls -li)证实了i-node号码是不同的。因此,不,这不是原因。
2.- 是否存在ln的别名或函数? 这很容易检查。只需在Bash中发出type -a ln即可。结果将显示bash解释ln为什么。如果它仅是文件/bin/ln,那么就是正确的。 您确认没有涉及别名或函数。

3.- 因为“脚本是从另一个目录运行的”。 这里的重点是:如果ln实际上创建了一个硬链接,那么文件系统中是否有其他文件具有相同的i-node号码。可以使用以下方式验证是否存在具有相同i-node号码的其他文件(使用列表中的inode号码53、54、56):

find / -follow -inum <your inum>

我希望您真正意识到config-b.properties实际上并不存在(作为一个文件)。编辑此类文件可能会破坏链接。
实际脚本是否也会更改/更新文件内容?
注01:请注意,K技巧确实可以在仅进行一次外部调用的情况下解析提取: http://www.charlestonsw.com/perl-regular-expression-k-trick/
ACTIVE_DB=$(grep -Po "^[ \t]*db.active[ ]+=[ ]+\K." config.properties)

已确认实际执行脚本中对config-b.properties进行的sed -i操作是问题的根源。


关于引号,我知道剪切的结果是“ A”。这就是为什么我之后要修剪它。至于其他部分,我回到工作岗位时会检查一下。感谢您提供的一些见解。 - Olivier Grégoire
type -a ln 的结果是 ln is /bin/ln。对我来说看起来没问题。我在问题中展示的片段是从我的脚本和配置文件中直接摘取的。我们确实有称为 AB 的数据库,它们可以互相替换。最后,我不明白你的第二点,请详细说明一下。 - Olivier Grégoire
命令“ln”无法执行您所报告的操作。应该发生其他事情。我将编辑我的答案以提供更多选项。 - user2350426

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接