Résoudre tous les alias dans une ligne de commande zsh

12

J'ai des alias imbriqués et je veux tous les résoudre avant d'exécuter la commande. Comment je fais ça?

S'il y a une fonction qui n'est liée à aucune touche, cela M-x foobarme convient aussi. Je pourrais même utiliser la commande externe ( type, command, which, peu importe). J'ai tout essayé depuis le fil Pourquoi ne pas utiliser "qui"? Que faut-il utiliser alors? mais rien ne fonctionne.

WeSenseASoulInSearchOfAnswers
la source
2
C-x adéveloppe l'alias sous le curseur (en supposant que vous utilisez le système de complétion).
Stéphane Chazelas
C'est vrai _expand_alias (^Xa): expands the word the cursor is on if it is an alias. C'est utile, mais c'est quand même dommage qu'en bash on puisse étendre toute la ligne mais pas en zsh.
WeSenseASoulInSearchOfAnswers
Je suppose qu'il est possible d'écrire une commande pouvant être liée qui invoque _expand_aliasjusqu'à ce que le tampon d'édition ne change plus.
vinc17

Réponses:

10

Notez que Ctrl-Alt-E dans bashne développe pas seulement les alias. Il développe également les variables, la substitution de commandes (!), La substitution de processus (!), L'expansion arithmétique et supprime les guillemets (il ne fait pas de génération de nom de fichier (globbing) ou d'expansion de tilde).

Il ne parvient pas toujours à étendre les alias. Donc, même s'il a ses utilisations, il est important de réaliser que son résultat change potentiellement la signification de la ligne de commande, a des effets secondaires et est potentiellement dangereux.

Par exemple dans:

$ a=';w' b=1
$ alias foo=bar
$ b=2; echo $b $a; cd /tmp/dir && for i do foo $(pwd) <(ls); done

Si j'appuie M-C-Eici, cela me donne:

$ b=2; echo 1 ;w; cd /tmp/dir && for i do foo / /dev/fd/63; done

Ce qui me donne une ligne de commande complètement différente (et imaginez ce qui se serait passé si j'avais eu rm -rf *au lieu de pwdci - dessus) et ne développe pas l' fooalias.

Avec zsh, pour s'appuyer sur la note de Gilles sur les alias développés à l'intérieur des fonctions, vous pourriez faire:

expand-aliases() {
  unset 'functions[_expand-aliases]'
  functions[_expand-aliases]=$BUFFER
  (($+functions[_expand-aliases])) &&
    BUFFER=${functions[_expand-aliases]#$'\t'} &&
    CURSOR=$#BUFFER
}

zle -N expand-aliases
bindkey '\e^E' expand-aliases

Cela ne développera les alias que si la ligne de commande actuelle est syntaxiquement valide (elle se double donc d'un vérificateur de syntaxe).

Contrairement à bashMCE, il résout également complètement les alias. Par exemple, si vous avez:

$ alias ll='ls -l'; alias ls='ls --color'
$ ll

Sera étendu à:

$ ls --color -l

Notez qu'il canonise également la syntaxe pour des choses comme:

$ for i (*) cmd $i; foo

sera changé en:

$ for i in *
        do
                cmd $i
        done
        foo
Stéphane Chazelas
la source
C'est buggy. Si j'ai alias ls='ls --color'et tape C-x adessus ls, j'obtiens: \ls --color(pour que le nouveau lsne soit pas mal interprété comme un alias). Mais avec votre expand-aliases, je reçois:, ls --colorrendant le résultat ambigu.
vinc17
@ vinc17, ce n'est pas ambigu en ce qu'il résout complètement l'alias (à cet égard, c'est moins bogué que l' bashéquivalent). Mais il est vrai que si vous exécutez la commande après cela, vous obtiendrez un autre cycle d'expansion d'alias (comme dans bash), donc idéalement, vous voudriez désactiver temporairement l'expansion d'alias, alors par exemple, enveloppez-le dans un (){ setopt localoptions noexpandalias; ...; }. Notez que vous pouvez dire que le _expand_aliasbuggy est aussi bien qu'il étend l'alias lorsqu'il est exécuté \ls.
Stéphane Chazelas
@ vinc17, cette barre oblique inversée effectuée par _expand_aliasest également facilement dupe comme alias 'foo=repeat 3 foo'ou alias ls='ls --color'; alias '\ls=echo fooled'. Il n'y a pas de solution parfaite ici.
Stéphane Chazelas
En ce qui concerne _expand_aliaspour alias 'foo=repeat 3 foo', je considère le manque backslash comme un bug. Et alias '\ls=echo fooled'ne devrait pas être autorisé; ici , je préfère bash, qui dit: bash: alias: '\ls': invalid alias name.
vinc17
@ vinc17, je ne vois pas comment cela peut être vu comme autre chose qu'une limitation dans bash. Si vous n'aimez pas les alias avec des barres obliques inverses, ne les utilisez pas, mais pourquoi voudriez-vous que le shell les rejette? Alors que les alias sont un remplacement de fonctions d'un pauvre homme dans csh (d'où ils viennent), dans les shells de type Bourne, ce sont des hacks pour faire des tours qui ne peuvent pas être faits avec des fonctions, une certaine forme d'expansion de macro qui s'accroche tôt dans l'analyseur shell , Je ne vois pas l'intérêt de restreindre ce qu'il peut faire.
Stéphane Chazelas
6

Si vous remplissez une ligne de commande dans une définition de fonction, puis imprimez la fonction, les alias seront développés. Vous obtiendrez également des espaces normalisés.

% alias foo='bar -1'
% alias bar='qux -2'
% f () foo -3
% which f
f () {
        qux -2 -1 -3
}

Pour mettre tout cela dans une commande interactive, vous pouvez créer un widget zle. Vous pouvez définir une fonction directement en insérant son code dans une entrée du functionstableau; vous obtiendrez l'effet de normalisation lorsque vous relirez.

normalize-command-line () {
  functions[__normalize_command_line_tmp]=$BUFFER
  BUFFER=${${functions[__normalize_command_line_tmp]#$'\t'}//$'\n\t'/$'\n'}
  ((CURSOR == 0 || CURSOR = #BUFFER)
  unset 'functions[__normalize_command_line_tmp]'
}
zle -N normalize-command-line
bindkey  normalize-command-line

Vous obtenez le même effet de normalisation dans le preexeccrochet . Les alias sont également développés au moment où une fonction est chargée automatiquement ( autoload -Uest couramment utilisée pour éviter l'expansion des alias).

La _expand_aliasfonction d'achèvement développe le mot sous le curseur s'il s'agit d'un alias. Il utilise le aliasestableau . Ce n'est pas récursif. Vous pouvez implémenter un expanseur d'alias plus général en utilisant aliases, mais c'est un peu difficile, car déterminer les emplacements où les alias sont développés est intimement lié à la syntaxe du shell.

Gilles 'SO- arrête d'être méchant'
la source
2
J'ai toujours utilisé autoload -Usimplement parce que la documentation zsh le recommande, mais je n'ai jamais compris ce -Uqui s'est réellement passé avant d'avoir lu ceci :). Aussi pour toute personne intéressée, on peut invoquer les _expand_alias fonctionnent directement en tapant votre alias dans la ligne de commande, frapper <Esc>, xpour lancer le mini - tampon, puis en tapant_expand_alias<Enter>
the_velour_fog
2

Si vous avez de nombreux alias fantaisie imbriqués et que vous n'êtes pas sûr de ce que zsh fait réellement avec eux et dans quel ordre les options sont passées à la commande, vous pouvez toujours démarrer zsh avec -xoption. Cela affichera les commandes et les arguments lors de leur exécution.

Sachez cependant que cette option est plutôt destinée au débogage, elle imprime donc beaucoup de choses inutiles juste après l' zsh -xinvocation (essentiellement chaque fonction / widget / plugin de votre .zshrc), et pendant l'exécution de la commande, elle peut également être verbeuse, en particulier si vous avez défini preexecet precmdaccroché.

Je dois également mentionner qu'il imprime uniquement les commandes qui sont finalement exécutées et que les commandes séparées sont imprimées séparément, donc après

alias a='echo a'
alias b='echo b'
alias c='echo c'
alias d='echo d'
a && b || c; d

Tu verras

+zsh:1> echo a
a
+zsh:1> echo b
b
+zsh:1> echo d
d
jimmij
la source