¿Cómo editar la finalización de comandos para ssh en zsh?

11

Me gustaría configurar la finalización del comando en zsh para mostrar los nombres de host después de escribir

ssh [TAB]

tomando los nombres de mi archivo .ssh / config (y preferiblemente de conocido_hosts y / etc / hosts y en cualquier otro lugar que tenga sentido) y presentando una sola lista.

Actualmente hace algo de esto, pero

  1. no usa .ssh / config en absoluto
  2. requiere un nombre de usuario primero, aunque el uso de .ssh / config hace que escribir nombres de usuario sea innecesario
  3. presenta varias listas (probablemente una de known_hosts y otra de / etc / hosts, pero no lo he verificado)

Por lo tanto, quiero incluir nombres de usuario conocidos, así como nombres de host conocidos en la lista (preferiblemente única) después de escribir ssh [TAB]

(Vengo aquí antes que Google porque 1) dará como resultado que la respuesta se almacene aquí, y 2) probablemente sea más eficiente. Si nadie más responde, buscaré la respuesta.)

iconoclasta
fuente
Creo que esta sería una pregunta para el superusuario
Etienne Perot,
En realidad, al mirarlo ahora debería haberlo preguntado en unix.stackexchange.com. (Pero no recuerdo si eso fue hace unos 2 años cuando lo pregunté). ¿Alguien quiere migrarlo?
iconoclasta

Respuestas:

16

Aquí está la parte relevante de mi .zshrc. No ha cambiado desde 2002, por lo que podría escribirlo de manera diferente hoy, pero aún funciona para completar los nombres de host desde ~/.ssh/configy ~/.ssh/known_hosts(si HashKnownHostsestá desactivado, no existía en esos días).

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- deja de ser malvado'
fuente
Bien, finalmente descubrí por qué esto no estaba funcionando. El problema no fue con el código que proporcionó: funciona bien, una vez que se resolvió el otro problema.
iconoclasta
1

La función que proporciona la sshfinalización se encuentra en /usr/share/zsh/functions/Completion/Unix/_sshmi sistema.

También vea la man zshcompsysdocumentación (especialmente haga una búsqueda en "host" que aparece en varios lugares y "ssh" que aparece en un par de lugares).

Es posible que agregar un zstylecomando a su ~/.zshrchaga lo que está buscando sin tener que modificar la función de finalización.

Pausado hasta nuevo aviso.
fuente
O en /usr/share/zsh/4.3.9/functions/_sshMac OS X (adapte la versión)
Studer
Obtengo ese archivo, escribo ssh <TAB> y obtengo la finalización del nombre de archivo. ¿Qué pasa con eso?
iconoclasta
1

Hago esto usando una lista de todos los hosts en un dominio dado usando dig. Puede reemplazar la siguiente función con el sistema de búsqueda que desee, incluido su archivo de hosts o una lista estática:

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

Nota: El código anterior podría no reemplazar completamente el sistema completo para el comando ssh en su configuración. Si tiene problemas con él, intente cambiar el comando "ssh" a otro comando aleatorio como "mycompletetest" y vea si la finalización funciona para eso.

¡También tenga en cuenta que esto hace la transferencia de zona dns en cada finalización! Si usa esto mucho o en un dominio bastante estático, tendría sentido hacer la búsqueda y guardar el resultado, entonces en su función de búsqueda simplemente configure reply = zone_result.

Caleb
fuente
No puedo hacer que eso funcione en absoluto. Obviamente tengo que cambiar la parte de respuesta, pero ¿puede dar un ejemplo de trabajo con texto estático, así que sé en qué formato debería estar? (Intenté lo que entendía que decía la página de manual, y corregí -k a -K, y todavía no funcionó)
Iconoclasta
El formato de respuesta es bastante simple, es solo un blob de texto directo, una respuesta posible por línea. Lo de dns es probablemente la parte difícil. Su servidor DNS debe admitir la transferencia de zona para que esto funcione. Esto generalmente significa que tiene que estar hablando con el servidor DNS principal para el dominio en cuestión, de esta manera dig axfr mydomain.com @ns1.mydomain.com. Asegúrese de que puede ejecutarlo manualmente y que parte de la salida debe ser una lista de hosts registrados en ese dominio y sus registros A o lo que sea. Eso era lo que estaba buscando, luego corté solo la parte del nombre de host, no el registro completamente calificado.
Caleb
Por favor, tenga en cuenta que he reescrito el código de ejemplo en mi respuesta para que funcione completamente la solución de cortar y pegar, incluido un nombre de dominio para el que funciona la búsqueda de transferencia de zona. Luego puede adaptarlo a la suite. Me disculpo por el original que terminó con dos errores tipográficos de cuando arranqué unas 20 capas adicionales de cosas que estaban en mi archivo .zshrc que no son relevantes para este ejemplo.
Caleb
El comando dig en backticks funciona ahora (¡gracias por editarlo!), Pero no muestra ninguno de los resultados de ese comando cuando escribo ssh <TAB>. ¿Hay algo más fuera de esto que deba habilitarse antes de que funcione?
iconoclasta
0

Me gusta mantener mi known_hostsarchivo hash y prefiero no HashKnownHostsapagarlo. Descubrí que sembrar lo que @Gilles tiene con lo que ya está en mi historia ha sido bastante efectivo para mis necesidades.

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

Además, FWIW, esto es lo que usé para 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
fuente