
Existe alguma maneira de criar um link que seja atualizado dinamicamente. Minha situação específica é que tenho vários exercícios (diretórios) e quero vincular ao mais recente:
exercises/
│ exercise_01/
│ │ files ...
│ exercise_02/
│ │ files ...
│ exercise_03/
│ │ files ...
│ exercise_latest/ -> exercise_03/
então se eu cd exercises/exercise_latest
sempre for para o mais recente. Adicionar um novo diretório exercise_04
faria com que o link apontasse para aquele.
As soluções que posso encontrar são:
- Um cron job que é executado uma vez por minuto e é vinculado novamente se detectar um novo diretório.
- Não faça dele um link, mas um script que
cd
leva ao diretório mais recente. - Mantendo-o manualmente
Nenhuma das soluções é particularmente elegante. 1.
é realmente ineficiente e potencialmente muito lento. Não consigo copiar arquivos para 2.
. 3.
derrota o propósito.
As versões de software versionadas fazem algo semelhante. Por exemplo, python3
sempre há links para a versão mais recente do Python 3. No entanto, eles provavelmente são atualizados sempre que uma nova versão é instalada.
Existe algo que eu possa fazer que seja mais elegante do que minhas ideias?
Responder1
A maneira como resolvi isso foi criando um agente launchctl/launchd que pode ouvir arquivos. Isso é específico do macOS, mas existem soluções semelhantes para 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>
Isso executa um script 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()
aqui LINK_ROOTS
contém todos os arquivos que devem ser vinculados. Todos os arquivos correspondentes a uma raiz de link com um sublinhado e quaisquer números atrás corresponderão, o maior deles será vinculado à raiz do link.
então se LINK_ROOTS = ['exercise']
o maior deles estiver vinculado assim exercise -> exercise_04
.
Esta não é uma solução perfeita, mas funciona de forma bastante confiável e não é superineficiente.