Estoy escribiendo una función que imita la finalización del host del comando ssh ; la principal diferencia es que utiliza los hosts del dominio xyz.com que se encuentra en ~/.ssh/known_hosts para la finalización.
Aquí está la función:
_xyzssh() { COMPREPLY=() local arg="${COMP_WORDS[COMP_CWORD]}" [[ $arg != -* ]] || return 0 [ -f ~/.ssh/known_hosts ] || return 0 local -a known_hosts read -d '' -a known_hosts < <(awk '{ n = split($1,arr,","); for (i = 1; i <= n; i++) if (arr[i] ~ /\.xyz\.com$/) print arr[i] }' ~/.ssh/known_hosts) if [[ $arg == *@* ]] then known_hosts=( ${known_hosts[@]/#/${arg%%@*}@} ) fi COMPREPLY=( $(compgen -W "${known_hosts[*]}" -- $arg) ) return 0 } complete -F _xyzssh xyzsshEste código funciona bien en Linux (bash 4.2) pero tiene un comportamiento extraño en macOS (bash 3.2). Por ejemplo, cuando escribo:
xyzssh -X user@server servidor
En Linux se completa con: xyzssh -X user@server.xyz.com
En macOS, "dobla" el nombre de usuario: xyzssh -X useruser@server.xyz.com
Supongo que hay algo sospechoso con el @ porque cuando escribo:
xyzssh -X user\@server servidor
Se completa correctamente tanto en Linux como en macOS.
¿Hay alguna forma de corregir este comportamiento en macOS? arreglado: vea la respuesta aceptada a continuación
Acabo de enterarme de COMP_WORDBREAKS , que contiene @ en macOS mientras que en Linux no.
Puedo solucionar el problema cuando configuro COMP_WORDBREAKS="${COMP_WORDBREAKS//@}" en el shell, pero hacer que el cambio sea resistente no es tan fácil en macOS (exportar esta configuración en mi .bash_profile o .bashrc no parece trabaja)...
Tengo otros problemas, esta vez con Solaris (bash 4.4), en los que ${COMP_WORDS[COMP_CWORD]} ni siquiera contiene la parte user@ cuando se llama a la función de finalización... Todavía estoy investigando cómo solucionarlo . arreglado: vea la respuesta aceptada a continuación
Es la primera vez que escribo una función de finalización, por lo que es posible que me esté perdiendo algo obvio.
Tomando el camino de responder a mi propia pregunta
COMPREPLY debe completarse de manera diferente según la presencia de @ en COMP_WORDBREAKS .
Por ejemplo, cuando el usuario escribe ssh user@server Tab :
@ no está en COMP_WORDBREAKS entonces COMPREPLY debería verse así: COMPREPLY=( "user@server.xyz.com" "user@server-bis.xyz.com" )@ está en COMP_WORDBREAKS entonces COMPREPLY debería verse así: COMPREPLY=( "@server.xyz.com" "@server-bis.xyz.com" )Una solución para hacer que la función de finalización sea compatible con ambos casos es agregar el prefijo correcto después de encontrar la lista de hosts coincidentes:
Actualización: problema fijo de Solaris bash 4.4
_xyzssh() { COMPREPLY=() local curr="${COMP_WORDS[COMP_CWORD]}" local prev="${COMP_WORDS[COMP_CWORD-1]}" [[ $curr == -* ]] && return 0 [ -f ~/.ssh/known_hosts ] || return 0 local -a known_hosts IFS=$'\n' read -r -d '' -a known_hosts < <(awk '{ n = split($1,arr,","); for (i = 1; i <= n; i++) if (arr[i] ~ /\.xyz\.com$/) print arr[i] }' ~/.ssh/known_hosts) COMPREPLY=( $(compgen -W "${known_hosts[*]}" -- "${curr#*@}") ) if [[ $curr == *@* || $prev == @ ]] then if [[ $COMP_WORDBREAKS == *@* ]] then COMPREPLY=( "${COMPREPLY[@]/#/@}" ) else COMPREPLY=( "${COMPREPLY[@]/#/${curr%%@*}@}" ) fi fi return 0 } complete -F _xyzssh xyzssh Esto es lo que obtengo en COMP_WORDS cuando escribo xyzssh -X user@server Tab en los diferentes sistemas operativos:
@ no presente en COMP_WORDBREAKS ) COMP_WORDS=( "xyzssh" "-X" "user@server" )@ presente en COMP_WORDBREAKS ) COMP_WORDS=( "xyzssh" "-X" "user@server" )@ presente en COMP_WORDBREAKS ) COMP_WORDS=( "xyzssh" "-X" "user" "@" "server" )