我可以使用什么幂等命令来建立指向目录的符号链接?


16

我想将命令放入外壳脚本中,该脚本将创建指向目录的符号链接,但是此脚本可以反复运行,因此在后续调用中,该命令不应更改任何内容。

这是目录结构:

% tree /tmp/test_symlink 
/tmp/test_symlink
├── foo
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

我想在foo/称为目录的片段中创建一个符号链接/tmp/test_symlink/repo/resources/snippets
所以我跑:

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

这给出了预期的结果。

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

5个目录,5个文件

但是,再次运行该命令时,

% ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/snippets
'/tmp/test_symlink/foo/snippets/snippets' -> '/tmp/test_symlink/repo/resources/snippets'

它创建到目录的符号链接,该目录中存在符号链接aleady,将符号链接放入真实目录中

% tree /tmp/test_symlink                                                          
/tmp/test_symlink
├── foo
   └── snippets -> /tmp/test_symlink/repo/resources/snippets
└── repo
    └── resources
        └── snippets
            ├── php.snippets
            ├── sh.snippets
            ├── snippets -> /tmp/test_symlink/repo/resources/snippets
            ├── snippets.snippets
            ├── sql.snippets
            └── vim.snippets

为什么会发生这种情况?如何修改命令,以免后续调用不会产生这种奇怪的效果?

Answers:


16

您应该-T为此使用选项,它告诉ln您始终将链接名称视为所需的链接名称,而不是目录。

如果没有此选项,则如果您提供ln一个作为目录存在的链接名称,它将在该目录内创建指向目标的新链接。

请注意,这-T是一种GNU-ism(至少,它不在POSIX中),但是您已经在使用-v,它也是一种GNU-ism,所以我想这不是问题。

另外,您可以仅将父目录指定为链接名称,并且该链接将始终在此处(重新)创建:

ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/

之所以有效,是因为您的符号链接与目标名称相同。


谢谢,是的,gnu选项不一定是可移植的,但它们确实有用-当不需要可移植性时。你的答案对我有用。从手册页-n, --no-dereference treat LINK_NAME as a normal file if it is a symbolic link to a directory-T, --no-target-directory treat LINK_NAME as a normal file always您认为始终将符号链接视为文件会更好吗?我以为最好限制使用这些“特殊”选项?
the_velour_fog

如果您知道它们可用,则没有理由避免使用特定于GNU的选项(IMO)。您要求一个幂等命令,-T即如何使ln幂等。如果您愿意,还有其他方法可以获得相同的结果,例如删除链接并重新创建它,或者如果您知道该链接foo已经存在ln -sfv /tmp/test_symlink/repo/resources/snippets /tmp/test_symlink/foo/(实际上,这可能是一个更好的答案,我将进行更新)。
斯蒂芬·基特

1
很酷,是的,我以前曾经使用if语句来检测符号链接的存在,如果符号链接存在,则跳过该命令,但是脚本最终变得混乱且难以阅读,我想要的是幂等但简单的东西,例如不使用mkdir -p测试,没有逻辑,只有一个衬里好:)
the_velour_fog

@Stephen Kitt:您提到了-T的用法,但是我在您的答案的代码片段中找不到它。我是误会还是错过了什么?
Guizmoo19年

@ Guizmoo03也许您错过了介绍该片段的“替代”。前三个段落进行了解释-T,但是我的答案的末尾提出了另一个不使用的解决方案-T
史蒂芬·基特

-1

最好的办法是,在第二遍中,该ln命令的行为类似于cpmv,即如果在<destination>处看到“目录”,则会将该文件放入其中,否则,如果<destination>则不存在存在,它将成为<destination>。(这是“ slashdot”技巧避免的-即,它将确保<destination>存在并且是目录)。
但是我可能是错的。

无论哪种情况,这似乎都有效

ln -sfv --no-dereference /tmp/test_symlink/repo/resources/snippets/ foo/snippets
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.