Comment éditer la complétion de commande pour ssh sur zsh?

11

Je voudrais configurer la complétion de commande sur zsh pour afficher les noms d'hôtes après avoir tapé

ssh [TAB]

prendre les noms de mon fichier .ssh / config (et de préférence à partir des hôtes connus et / etc / hosts et partout ailleurs qui a du sens) et présenter une seule liste.

Il fait une partie de cela actuellement, mais

  1. il n'utilise pas du tout .ssh / config
  2. il nécessite d'abord un nom d'utilisateur, même si l'utilisation de .ssh / config rend la saisie des noms d'utilisateur inutile
  3. il présente plusieurs listes (probablement une de known_hosts et une autre de / etc / hosts, mais je ne l'ai pas vérifié)

Je veux donc inclure les noms d'utilisateurs connus ainsi que les noms d'hôtes connus dans la liste (de préférence unique) après avoir tapé ssh [TAB]

(Je viens ici avant Google parce que 1) cela entraînera le stockage de la réponse ici, et 2) c'est probablement plus efficace. Si personne d'autre ne répond, je traquerai la réponse.)

iconoclaste
la source
Ce serait une question pour le superutilisateur, je pense
Etienne Perot
En fait, en le regardant maintenant, j'aurais dû le demander sur unix.stackexchange.com. (Mais je ne me souviens pas si c'était il y a environ 2 ans quand je l'ai demandé.) Quelqu'un veut-il le migrer?
iconoclaste du

Réponses:

16

Voici la partie pertinente de mon .zshrc. Il n'a pas changé depuis 2002, donc je pourrais l'écrire différemment aujourd'hui, mais il fonctionne toujours pour compléter les noms d'hôte de ~/.ssh/configet ~/.ssh/known_hosts(s'il HashKnownHostsest désactivé - il n'existait pas à l'époque).

h=()
if [[ -r ~/.ssh/config ]]; then
  h=($h ${${${(@M)${(f)"$(cat ~/.ssh/config)"}:#Host *}#Host }:#*[*?]*})
fi
if [[ -r ~/.ssh/known_hosts ]]; then
  h=($h ${${${(f)"$(cat ~/.ssh/known_hosts{,2} || true)"}%%\ *}%%,*}) 2>/dev/null
fi
if [[ $#h -gt 0 ]]; then
  zstyle ':completion:*:ssh:*' hosts $h
  zstyle ':completion:*:slogin:*' hosts $h
fi
Gilles 'SO- arrête d'être méchant'
la source
D'accord, j'ai finalement compris pourquoi cela ne fonctionnait pas. Le problème n'était pas avec le code que vous avez fourni: cela fonctionne très bien, une fois que l'autre problème a été résolu.
iconoclaste
1

La fonction qui fournit l' sshachèvement se trouve /usr/share/zsh/functions/Completion/Unix/_sshsur mon système.

Voir aussi la man zshcompsysdocumentation (surtout faire une recherche sur "host" qui apparaît à plusieurs endroits et "ssh" qui apparaît à quelques endroits).

Il est possible que l'ajout d'une zstylecommande à votre ~/.zshrcfonction fasse ce que vous recherchez sans avoir à modifier la fonction d'achèvement.

En pause jusqu'à nouvel ordre.
la source
Ou dans /usr/share/zsh/4.3.9/functions/_sshMac OS X (adapter la version)
Studer
Je source ce fichier et tape ssh <TAB> et j'obtiens l'achèvement du nom de fichier. Quoi de neuf avec ça?
iconoclaste du
1

Je fais cela en utilisant une liste de tous les hôtes sur un domaine donné en utilisant dig. Vous pouvez remplacer la fonction ci-dessous par le système de recherche que vous souhaitez, y compris votre fichier d'hôtes ou une liste statique:

function complete_host_from_zone () {
    reply=(`dig axfr ouraynet.com @ns1.ouraynet.com | grep -e '^[a-z]' | cut -d\. -f1`)
}
compctl -x 'p[1]' -K complete_host_from_zone -- ssh

Remarque: Le code ci-dessus peut ne pas remplacer complètement le système complet de la commande ssh dans votre configuration. Si vous avez des problèmes, essayez de changer la commande "ssh" en une autre commande aléatoire comme "mycompletetest" et voyez si la complétion fonctionne pour cela.

Notez également que cela effectue le transfert de la zone DNS à chaque achèvement! Si vous l'utilisez beaucoup ou sur un domaine assez statique, il serait judicieux de faire la recherche et d'enregistrer le résultat, puis dans votre fonction de recherche, définissez simplement reply = zone_result.

Caleb
la source
Je ne peux pas faire fonctionner ça du tout. Évidemment, je dois changer la partie réponse, mais pouvez-vous donner un exemple de travail avec du texte statique, donc je sais dans quel format il devrait être? (J'ai essayé ce que j'ai compris la page de manuel à dire, et corrigé de -k à -K, et cela n'a toujours pas fonctionné.)
iconoclaste
Le format de réponse est assez simple, c'est juste un blob de texte direct, une réponse possible par ligne. La chose DNS est probablement la partie délicate. Votre serveur DNS doit prendre en charge le transfert de zone pour que cela fonctionne. Cela signifie généralement que vous devez parler au serveur DNS principal pour le domaine en question, comme ceci dig axfr mydomain.com @ns1.mydomain.com. Assurez-vous que vous pouvez l'exécuter manuellement et qu'une partie de la sortie doit être une liste d'hôtes enregistrés sur ce domaine et leurs enregistrements A ou autres. C'est ce que je recherchais, puis je ne supprimais que la partie du nom d'hôte et non l'enregistrement complet.
Caleb
Veuillez noter que j'ai réécrit l'exemple de code dans ma réponse pour que la solution de copier-coller fonctionne pleinement, y compris un nom de domaine pour lequel la recherche de transfert de zone fonctionne. Vous pouvez ensuite adapter à la suite. Je m'excuse pour l'original qui s'est retrouvé avec deux fautes de frappe lorsque j'ai extrait environ 20 couches supplémentaires de choses qui étaient dans mon fichier .zshrc et qui ne sont pas pertinentes pour cet exemple.
Caleb
La commande dig dans les backticks fonctionne maintenant (merci de la modifier!), Mais elle ne montre aucun résultat de cette commande lorsque je tape ssh <TAB>. Y a-t-il autre chose en dehors de cela qui doit être activé avant que cela fonctionne?
iconoclaste du
0

J'aime garder mon known_hostsfichier haché et préfère ne pas l' HashKnownHostséteindre. J'avais trouvé que semer ce que @Gilles a avec ce qui est déjà dans mon histoire a été assez efficace pour mes besoins.

h=($(echo $(history | awk '{print $4 " " $5 "\n"}' | grep 'ssh ' | awk '{print $2}' | sort -u)))
if [[ -r ~/.ssh/config ]]; then
  h=($h ${${${(@M)${(f)"$(cat ~/.ssh/config)"}:#Host *}#Host }:#*[*?]*})
fi
if [[ -r ~/.ssh/known_hosts ]]; then
   h=($h ${${${(f)"$(cat ~/.ssh/known_hosts{,2} || true)"}%%\ *}%%,*}) 2>/dev/null
fi
if [[ $#h -gt 0 ]]; then
  zstyle ':completion:*:ssh:*' hosts $h
  zstyle ':completion:*:slogin:*' hosts $h
fi

Aussi, FWIW, c'est ce que j'ai utilisé pour Bash:

# SSH Autocompletion
complete -W "
  $(echo $(grep '^\s*ssh ' ~/.bash_history | sort -u | sed 's/^ssh //' | awk '{print $1}'))
  $(echo $(history | awk '{print $2 " " $3}' | grep 'ssh ' | awk '{print $2}' | sort -u))
  $(sed 's/#.*//;' ~/.ssh/config | awk ' /^Host (.+)$/ {$1 = "";print tolower($0)}')
" ssh
Karl Wilbur
la source