动态链接目录

动态链接目录

有什么方法可以创建一个动态更新的链接吗?我的具体情况是,我有多个练习(目录),我想链接到最新的一个:

exercises/
│   exercise_01/
│   │    files ...
│   exercise_02/
│   │    files ...
│   exercise_03/
│   │    files ...
│   exercise_latest/ -> exercise_03/

所以如果我cd exercises/exercise_latest总是转到最新的目录。添加新目录exercise_04将使链接指向该目录。

我能想到的解决方案是:

  1. 每分钟运行一次的 cron 作业,如果检测到新目录则重新链接。
  2. cd不要使其成为链接,而要使其成为指向最新目录的脚本。
  3. 手动维护

这两种解决方案都不是特别优雅。1.效率真的很低,而且可能太慢。我无法将文件复制到2.3.违背了目的。

版本化的软件发布也有类似功能。例如,python3始终链接到最新的 Python 3 版本。不过,每次安装新版本时,它们可能都会更新。

我能做些比我的想法更优雅的事情吗?

答案1

我解决这个问题的方法是创建一个可以监听文件的 launchctl / launchd 代理。这是 macOS 特有的,但 Linux 也有类似的解决方案。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd>
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.me.exercise_listener.plist</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/python3</string>
        <string>/path/to/exercises/.ln_monitor.py</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/path/to/exercises/</string>
    </array>
    <key>StandardOutPath</key>
    <string>/path/to/exercises/.ln_monitor.log</string>
    <key>StandardErrorPath</key>
    <string>/path/to/exercises/.ln_monitor.log</string>
</dict>
</plist>

这将执行 Python 脚本 ( .ln_monitor.py)

#!/usr/bin/env python3

import os
import re
from pathlib import Path
import time


DIR = Path(os.path.dirname(os.path.realpath(__file__)))
LINK_ROOTS = ['exercise']
VERBOSE = True


def cprint(*args):
    if VERBOSE:
        print(*args)


def main():
    for root in LINK_ROOTS:
        regex = r'^' + root + r'_\d+$'
        matches = [
            directory for directory in DIR.iterdir()
            if directory.is_dir() and
            re.match(regex, directory.name)]
        match_link = DIR / root
        if matches:
            match = sorted(matches)[-1]
            cprint(f'link to {match.name}')
            if match_link.is_symlink():
                if match_link.resolve() == match:
                    cprint('is already linked')
                    continue
                match_link.unlink()
                cprint('unlinking')
                time.sleep(0.5)
            else:
                cprint('no link yet')
            match_link.symlink_to(match)
            cprint('linked')


if __name__ == '__main__':
    main()

这里LINK_ROOTS包含了所有要链接的文件。所有匹配带有下划线的链接根和后面任意数字的文件都将匹配,最大的文件将链接到链接根。

所以如果LINK_ROOTS = ['exercise']最大的一个将像这样链接起来exercise -> exercise_04

这不是一个完美的解决方案,但它运行起来相当可靠,而且效率也不是特别低。

相关内容