
Есть ли способ создать ссылку, которая будет динамически обновляться? Моя конкретная ситуация такова, что у меня есть несколько упражнений (каталогов), и я хочу сделать ссылку на последний:
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
.
Это не идеальное решение, но оно работает достаточно надежно и не слишком эффективно.