Trois lecteurs de ~/.ssh/config tranchaient la même question autrement : l'un comparait le mot-clé avec la casse, un autre exigeait un espace là où une tabulation est légale, un troisième laissait passer le motif nié `!nom`. Un alias déclaré « host » en minuscules était donc vu par l'un et invisible aux deux autres, ce qui se lit comme une panne intermittente de la découverte. La décision passe dans un module qui ne lit aucun fichier. Il rend None sur ce qui n'est pas une déclaration et une liste — possiblement vide — sur ce qui en est une : « Host * » ne nomme aucune machine mais reste une déclaration, sinon ses directives se rattachent au bloc précédent. Vérifié : 19 tests, et le menu reconnaît toujours le serveur du réseau par sa source SSH. --- EN --- Three readers of ~/.ssh/config settled the same question differently: one compared the keyword case-sensitively, another required a space where a tab is legal, a third let the negated pattern `!name` through. An alias declared with a lowercase "host" was therefore seen by one and invisible to the other two, which reads as intermittent discovery failure. The decision moves into a module that reads no file. It returns None for what is not a declaration and a list — possibly empty — for what is: "Host *" names no machine yet remains a declaration, otherwise its directives attach to the previous block. Checked: 19 tests, and the menu still recognises the server on the network through its SSH source. Assisted-by: Claude Opus 5
51 lines
2.3 KiB
Python
51 lines
2.3 KiB
Python
#!/usr/bin/env python3
|
|
# © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
"""Ce qu'est un nom de machine dans ~/.ssh/config, décidé en un seul endroit.
|
|
|
|
Trois lecteurs de ce fichier vivent dans ce dépôt — l'énumération des alias,
|
|
le menu de montage qui veut aussi l'adresse, et l'inventaire des entrées de
|
|
VM — et ils divergeaient sur la MÊME question. L'un comparait le mot-clé avec
|
|
la casse, un autre exigeait un espace là où une tabulation est légale, un
|
|
troisième laissait passer le motif nié. Un alias déclaré `host exo` était donc
|
|
vu par l'un et invisible aux autres, ce qui se lit comme une panne
|
|
intermittente de la découverte.
|
|
|
|
Le module ne lit aucun fichier et ne suit pas `Include` : il tranche une
|
|
ligne, rien de plus. Un alias déclaré dans un fichier inclus reste donc
|
|
invisible aux trois lecteurs, alors même que `ssh -G` le résoudrait — la
|
|
source est incomplète sans être fausse.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
# Le mot-clé, suivi d'au moins un blanc. ssh_config ne distingue pas la casse
|
|
# de ses mots-clés, et sépare par espace OU tabulation. Le blanc est exigé
|
|
# sans quoi `HostName` serait lu comme une déclaration d'hôte.
|
|
DECLARATION = re.compile(r"^[ \t]*host[ \t]+", re.IGNORECASE)
|
|
|
|
|
|
def declared_names(line) -> list | None:
|
|
"""Les noms de machine d'une ligne « Host », ou None si ce n'en est pas une.
|
|
|
|
Rend None pour toute autre ligne, et une liste — possiblement VIDE —
|
|
quand la ligne est une déclaration. Distinguer les deux compte : `Host *`
|
|
EST une déclaration qui ne nomme aucune machine, et un lecteur qui
|
|
accumule un bloc doit clore le précédent malgré tout. Rendre `[]` dans
|
|
les deux cas rattachait les directives d'un bloc générique au bloc
|
|
précédent.
|
|
|
|
Trois formes ne désignent aucune machine et sortent : les jokers `*` et
|
|
`?`, qui décrivent une règle appliquée à plusieurs hôtes, et le motif NIÉ
|
|
`!nom`, qui RETIRE un nom de l'ensemble que la ligne vient de décrire.
|
|
Retenir un motif nié rend une cible dont le nom commence par `!`, que ssh
|
|
ne résoudra jamais.
|
|
"""
|
|
if not DECLARATION.match(line or ""):
|
|
return None
|
|
return [
|
|
nom
|
|
for nom in line.split()[1:]
|
|
if "*" not in nom and "?" not in nom and not nom.startswith("!")
|
|
]
|