有什么方法可以创建一个动态更新的链接吗?我的具体情况是,我有多个练习(目录),我想链接到最新的一个:
exercises/
│ exercise_01/
│ │ files ...
│ exercise_02/
│ │ files ...
│ exercise_03/
│ │ files ...
│ exercise_latest/ -> exercise_03/
所以如果我cd exercises/exercise_latest
总是转到最新的目录。添加新目录exercise_04
将使链接指向该目录。
我能想到的解决方案是:
- 每分钟运行一次的 cron 作业,如果检测到新目录则重新链接。
cd
不要使其成为链接,而要使其成为指向最新目录的脚本。- 手动维护
这两种解决方案都不是特别优雅。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
。
这不是一个完美的解决方案,但它运行起来相当可靠,而且效率也不是特别低。