Est-il possible de configurer la façon dont bash complète les noms de répertoire?

8

Je voudrais demander à bash d'utiliser une méthode spéciale pour effectuer la complétion sur certains noms de répertoire. Par exemple, bash appellerait un de mes programmes pour effectuer la complétion si un chemin commence par "$$", et effectuer la complétion normalement sinon.

Est-ce possible? Comment le mettriez-vous en œuvre?

Bounty : J'apprécierais vraiment une réponse à cette question. Le but est de permettre au saut automatique de terminer les chemins pour toutes les commandes lorsque l'utilisateur les démarre avec un certain préfixe. Ainsi, par exemple, lors de la copie d'un fichier à partir d'un répertoire distant, vous pouvez taper:

cp $$patern + <Tab>

et le saut automatique se terminerait

cp /home/user/CompliCatedDireCTOry/long/path/bla/bla

et vous auriez juste à ajouter où vous voulez mettre le fichier. Bien sûr, je peux utiliser le commentaire d'ott pour l'ajouter à quelques commandes spécifiques, mais si quelqu'un a une meilleure idée, je serais très reconnaissant.

Peltier
la source
Voir la section "Achèvement programmable" dans la page de manuel bash.
Si vous votez pour clore ma question, veuillez expliquer pourquoi.
Peltier
2
"complete" prend en charge "-G pattern" pour faire correspondre votre $$ et le début et "-F func" pour appeler votre propre fonction, mais il a besoin d'un ou plusieurs noms de commande pour fonctionner.
Pourquoi ne pas simplement utiliser une variable d'environnement? Par exemple: cp $ prefix / file / path / to / dest /
Xenoactive
@Xenoactive: parce que le saut automatique est entièrement automatisé et fonctionne sur plusieurs chemins. L'utilisation de variables d'environnement définies manuellement n'aide pas. Ou peut-être avez-vous une idée puissante que je ne comprends pas?
Peltier

Réponses:

3

Vous pouvez le faire en remplaçant la liaison par défaut pour TAB (^ i). Vous devez d'abord remplacer la liaison TAB, puis vous devez créer une fonction qui appelle votre commande, enfin vous devez prendre la sortie de cette commande et mettre à jour la variable qui contient la ligne de commande actuelle.

Cette fonction prend la ligne de commande actuelle et change les deux derniers caractères en «huugs».

function my_awesome_tab_completion_function () {
  set -- $READLINE_LINE
  command="$1"
  shift
  argument="$*"
  argument_length=$(echo -n $argument | wc -c)
  if echo $argument | grep '^$$' >/dev/null 2>&1; then
    new_argument=$(echo $argument | sed 's/..$/huugs/') # put your autojump here
  else
    new_argument=$(compgen -d $argument)
  fi
  new_argument_length=$(echo -n $new_argument | wc -c)
  READLINE_POINT=$(( $new_argument_length - $argument_length + $READLINE_POINT ))
  READLINE_LINE="$command $new_argument"
}

Pour votre exemple, vous voudrez probablement changer la ligne new_argument pour ressembler à ceci:

  new_argument=$(autojump $argument)

Remplacez maintenant la liaison ^ i:

$ bind -x '"\C-i"':'my_awesome_tab_completion_function'

Maintenant, testez que cela fonctionne:

$ cd /ro<TAB>
changes my command to:
$ cd /root

donc l'achèvement normal fonctionne toujours, vous pouvez tester la partie $$ en faisant cd $$ ... etc

Si vous rencontrez des problèmes, activez le mode détaillé:

$ set -x

Il imprimera tout ce que fait la fonction.

J'ai testé cela sur Ubuntu 11 en utilisant bash 4.2.8 (1) -release (la valeur par défaut).

polynôme
la source
Il me semble que cela ne fonctionne que sur le premier paramètre de la commande, et peut également faire des trucs bizarres si l'onglet est utilisé de manière inattendue (par exemple après un $$$ ou après le nom de la commande), ou je me trompe?
harrymc
Eh bien, il passe $ * donc il "fonctionnera" plus que le premier paramètre (je ne connais pas du tout le saut automatique donc je ne suis pas sûr qu'il puisse prendre plusieurs arguments). Vous pouvez passer à la fin et n'envoyer que le dernier à saut automatique, ou utiliser READLINE_POINT pour déterminer où se trouve le curseur parmi les arguments de commande et envoyer uniquement ce «mot» à saut automatique.
polynôme
Votre solution est intéressante, mais vous devez admettre que la prise en charge de la touche de tabulation est un peu extrême. Il n'y a aucun moyen d'anticiper tous les cas possibles.
harrymc
Merci pour votre réponse, c'est une bonne solution alternative, et elle répond à la partie "pour toutes les commandes" de ma question. Je suis d'accord avec harrymc que c'est un peu extrême, cependant ... Je suppose que si personne ne propose une meilleure solution, je vais essayer et voir si elle est viable.
Peltier
Oui, je suis également d'accord avec harrymc, mais j'essayais de répondre à la question telle que posée. Personnellement, je lierais simplement cela à une autre touche de contrôle comme ^ G ou quelque chose pour que je puisse laisser l'onglet seul mais utiliser cette fonctionnalité.
polynôme
1

La routine de complétion bash peut être programmée comme un script shell.

Voici un exemple de script shell qui remplacera n'importe quel paramètre $$[Tab]par my replacement string, mais uniquement pour la commande spécifique mycommandet uniquement si le paramètre est exactement "$$":

_mycomplete()
{
        if [ ${COMP_WORDS[COMP_CWORD]} == \$\$ ]
        then
                COMPREPLY='my replacement string'
        fi
}
complete -o default -o bashdefault -F _mycomplete mycommand

Vous devez source le script à bash via source <file-name>(ou la commande dot) pour le démarrer, puis:

mycommand $$[Tab] -> mycommand my replacement string
mycommand $$$[Tab] -> mycommand $$$ (beep)
mycommand whatever[Tab] -> (will complete "whatever" in the normal bash manner)

Pour toujours le faire fonctionner pour certains ou tous les utilisateurs, incluez-le dans l'une des routines de profil bash .

Le problème avec la completecommande, c'est qu'elle ne fonctionnera que pour un ou plusieurs noms de commandes, qui sont spécifiés comme paramètres. On pourrait simplement lui donner la liste de toutes les commandes qui pourraient éventuellement être utilisées par les utilisateurs, ou dans des cas désespérés se développer /bin/* /usr/bin/* ~/bin/*.

Testé sur CentOS 5.5.

Ce script simple est basé sur les sources que j'ai énumérées dans mon autre réponse - celle qui a été supprimée par le modérateur studiohack. Si vous êtes intéressé, demandez-lui simplement de le supprimer.

harrymc
la source
Merci pour votre réponse. Ma question n'était pas vraiment à ce sujet depuis que je suis nouveau, c'était possible, mais ne terminera pas toutes les commandes. Cependant, je pense que si la solution de polynôme ne s'avère pas acceptable, je vais implémenter quelque chose comme ça et essayer de cibler les commandes les plus courantes.
Peltier