I have nested aliases and I want to resolve all of them before executing the command. How do I do that?

If there is a function that is not bound to any keys then M-x foobar is fine for me too. I could even use external command (type, command, which, whatever). I tried everything from the thread Why not use "which"? What to use then? but nothing works.

  • 2
    C-x a expands the alias under the cursor (assuming you're using the completion system). – Stéphane Chazelas Aug 17 '14 at 15:21
  • Right, _expand_alias (^Xa): expands the word the cursor is on if it is an alias. That is helpful, however still its a pity that in bash one can expand whole line but in zsh not. – WeSenseASoulInSearchOfAnswers Aug 17 '14 at 15:54
  • I suppose that it is possible to write a bindable command that invokes _expand_alias until the edit buffer no longer changes. – vinc17 Aug 17 '14 at 16:39

Note that Ctrl-Alt-E in bash does not only expand aliases. It also expands variables, command substitution (!), process substitution (!), arithmetic expand and removes quotes (it doesn't do filename generation (globbing) or tilde expansion).

It doesn't always manage to expand aliases. So while it has its uses, it's important to realise its outcome potentially changes the meaning of the command line, has side effects and is potentially dangerous.

For instance in:

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

If I press M-C-E here, that gives me:

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

Which gives me a completely different command line altogether (and imagine what would have happened if I had had rm -rf * instead of pwd above) and doesn't expand the foo alias.

With zsh, to build up on Gilles' note on aliases expanded inside functions, you could do:

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

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

That will expand the aliases only if the current command line is syntactically valid (so it doubles as a syntax checker).

Contrary to bash's M-C-E, it also resolves the aliases fully. For instance if you have:

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

Will be expanded to:

$ ls --color -l

Note that it also canonicalises the syntax so things like:

$ for i (*) cmd $i; foo

will be changed to:

$ for i in *
                cmd $i
  • This is buggy. If I have alias ls='ls --color' and type C-x a over ls, I get: \ls --color (so that the new ls isn't misinterpreted as an alias). But with your expand-aliases, I get: ls --color, making the result ambiguous. – vinc17 Aug 18 '14 at 11:12
  • @vinc17, it's not ambiguous in that it resolves the alias fully (in that regards, it's less buggy than the bash equivalent). But it's true if you run the command after that, you'll get another round of alias expansion (like in bash), so ideally you'd want to temporarily disable alias expansion, so for instance wrap that in a (){ setopt localoptions noexpandalias; ...; }. Note that you could say the _expand_alias is buggy as well as it expands the alias when run on \ls. – Stéphane Chazelas Aug 18 '14 at 11:31
  • @vinc17, that backslash escaping done by _expand_alias is also easily fooled like alias 'foo=repeat 3 foo' or alias ls='ls --color'; alias '\ls=echo fooled'. There is no perfect solution here. – Stéphane Chazelas Aug 18 '14 at 11:37
  • Concerning _expand_alias for alias 'foo=repeat 3 foo', I would regard the missing backslash as a bug. And alias '\ls=echo fooled' shouldn't be allowed; here I prefer bash, which says: bash: alias: '\ls': invalid alias name. – vinc17 Aug 18 '14 at 11:48
  • @vinc17, I can't see how that can be seen as anything else but a limitation in bash. If you don't like aliases with backslashes, don't use them, but why would you want the shell to reject them? While aliases are a poor man's functions replacement in csh (where they come from), in Bourne-like shells, they are hacks to do tricks that can't be done with functions, some form of macro expansion that hooks early in the shell parser, I don't see the point in restricting what it can do. – Stéphane Chazelas Aug 18 '14 at 16:21

If you stuff a command line into a function definition and then print out the function, the aliases will be expanded. You'll also get normalized whitespace.

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

To put all this into an interactive command, you can make a zle widget. You can define a function directly by stuffing its code into an entry in the functions array; you'll get the normalization effect when you read back.

normalize-command-line () {
  ((CURSOR == 0 || CURSOR = #BUFFER)
  unset 'functions[__normalize_command_line_tmp]'
zle -N normalize-command-line
bindkey … normalize-command-line

You get the same normalization effect in the preexec hook. Aliases are also expanded at the time a function is autoloaded (autoload -U is commonly used to avoid alias expansion).

The _expand_alias completion function expands the word under the cursor if it's an alias. It uses the aliases array. It is not recursive. You could implement a more general alias expander using aliases, but it is somewhat difficult, because figuring out the locations where aliases are expanded is intimately connected with shell syntax.

  • 2
    I always used autoload -U simply because the zsh documentation recommends it, but I never understood what -U actually did until I read this :). Also for anyone interested, one can invoke the _expand_alias function directly by typing your alias into the command line, hitting <Esc>, x, to launch the minibuffer, then typing _expand_alias<Enter> – the_velour_fog May 6 '17 at 8:45

If you have many nested fancy aliases and you are not sure what zsh is actually doing with them and in which order options are passed to command then you can always start zsh with -x option. This will print commands and arguments as they are executed.

Be aware however that this option is intended rather for debugging purpose so it prints a lot of useless stuff just after zsh -x invocation (basically each and every function/widget/plugin of your .zshrc), and during command execution it could be also verbose, especially if you have defined preexec and precmd hook.

I should also mention, that it prints only those commands which are finally executed, and separated commands are printed separately so after

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

You will see

+zsh:1> echo a
+zsh:1> echo b
+zsh:1> echo d

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.