4

For example, if I have a

alias dbmigrate='rails db:migrate'

is there a mode or simple way to configure in Bash and Zsh so that when I type in the shell:

$ dbmigrate  # press Enter

it will echo what the alias expands to before running it?

2
  • In fish, you can use abbr instead of alias. It will expand the abbreviated commands upon execution.
    – annahri
    Commented Oct 10, 2020 at 1:47
  • Late to the party, but this may be useful for those who stumble across this from a search engine. If you want to know what the command does without executing you can call which dbmigrate. This will echo something like dbmigrate: aliased to rails db:migrate
    – vivalldi
    Commented May 17, 2021 at 14:13

4 Answers 4

4

In bash, there are the alias-expand-line and history-and-alias-expand-line readline functions, but they aren't bound to any keys by default. You can bind them yourself -- example with Control-T:

bind '"\C-T": history-and-alias-expand-line'

Trying to bind Enter itself is would be messy, because not anytime you press Enter you have the command executed (eg. not when it's an incomplete pipeline, etc).

There's also the DEBUG trap, but determining whether the command was actually entered by the user (eg. not started from PROMPT_COMMAND or from a command substitution in PS1), and whether it was alias-expanded is going to be tricky; but if you're OK with it printing any command before being executed:

trap 'echo "> $BASH_COMMAND"' DEBUG

bash$ pwd
> pwd

In zsh, you can simply define a preexec function which prints the expanded command if different from the one entered by the user:

preexec(){ [ $1 != $2 ] && print -r "> $2" }

zsh$ j
> jobs

Beware however that some alias + history tricks won't work properly if you force-expand the alias. For instance, I have an c alias in bash to do calculations like c 3.17 * 4.2, without having to escape the *, (, etc:

alias c='_c=$(fc -nl -0); bc -l <<<${_c#*c} #'

Once readline has expanded it, the current line from the history (the one returned by fc -nl -0) will be the expanded, not the original command, and everything will fall apart.

2
  • my question is not really about "binding" something to the Enter key... it just means, can Bash and Zsh echo what command it is running before running it Commented Apr 5, 2020 at 20:11
  • 2
    And my answer shows you how to do that (with the DEBUG trap in bash and with preexec in zsh) shows some of their limitations and gotchas, and a possible alternative. You know, you're like, huh, supposed to read it before commenting on it. If you know a way to also get the unexpanded command in bash (just like in zsh, with the 2nd argument of preexec) just show it. I'm interested too!
    – user313992
    Commented Apr 6, 2020 at 10:08
3

Aliases are just simple substitutions. If you want to do something fancier, like print a message before running the command, you need something more powerful: a function. If you want the definition to look like an alias definition, you can define a function that defines a function.

function verbose_alias {
  local name=${1%%=*} expansion=${1#*=}
  eval "function $name {
    printf >&2 '%s is an alias for %s\\n' \"$name\" \"$expansion\";
    $expansion \"\$@\";
  }"
}
verbose_alias dbmigrate='rails db:migrate'
1
  • but I am not looking for a function... I want a way so that my alias is expanded and shown as to what is going to be done Commented May 17, 2021 at 18:31
2

POSIX requires the set command to have this option

-x

   The shell shall write to standard error a trace for each command after it expands the command and before it executes it. It is unspecified whether the command that turns tracing off is traced.

https://pubs.opengroup.org/onlinepubs/009604499/utilities/set.html

So you can just use set -x or set -o xtrace on any compatible shells. In bash you can see this in the man page:

-x

   After expanding each simple command, for command, case command, select command, or arithmetic for command, display the expanded value of PS4, followed by the command and its expanded arguments or associated word list.

xtrace

   Same as -x.

https://linux.die.net/man/1/bash

zsh also has a similar option for the set builtin

XTRACE (-x, ksh: -x)

   Print commands and their arguments as they are executed.

https://linux.die.net/man/1/zshoptions

You can also specify the -x option while executing scripts or commands

bash -x -c "echo This will be printed before running; ll"
bash -x script-to-be-debugged.sh
zsh -x -c "...; 3; md did-you-see-the-aliases"
zsh -x buggy-script.sh

Beware that it also expands the commands that are run before and after each prompt so it may be not desirable in many situations, for example when you use git variables in your PS1. In my clean Ubuntu VM only the running command is printed out

See also What does set -x do?

Another option is set -v or set -o verbose which doesn't expand the variables before printing

0

That kind of help is not available by default in bash.

The two related functions that are available by default are:

  • shopt -s histverify

If set, and readline is being used, the results of history substitution are not immediately passed to the shell parser. Instead, the resulting line is loaded into the readline editing buffer, allowing further modification.

Which addresses the part of an action when Enter is pressed, but only with an history expansion on the command line. For example:

$ shopt -s histverify
$ echo "this is a test"
This is a test
$ echo !!
$ echo echo "this is a test"  <---- expanded by the simple use of enter!! 
  • Readline shell-expand-line

shell-expand-line (M-C-e)

Expand the line as the shell does. This performs alias and history expansion as well as all of the shell word expansions. See HISTORY EXPANSION below for a description of history expansion.

This is exactly what you are asking for to be done, but the key to make it work is Ctrl-Alt-e

I am not aware of a solution that works with enter and executes shell-expand-line, sorry.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .