14

In bash, from inside PROMPT_COMMAND, is there a way to tell if the user just hit 'return' and didn't enter a command?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
user
  • 357

4 Answers4

8

Check whether the history number was incremented. A cancelled prompt or a prompt where the user just pressed Enter won't increment the history number.

The history number is available in the variable HISTCMD, but this is not available in PROMPT_COMMAND (because what you want there is in fact the history number of the previous command; the command that executes PROMPT_COMMAND itself has no history number). You can get the number from the output of fc.

prompt_command () {
  HISTCMD_previous=$(fc -l -1); HISTCMD_previous=${HISTCMD_previous%%$'[\t ]'*}
  if [[ -z $HISTCMD_before_last ]]; then
    # initial prompt
  elif [[ $HISTCMD_before_last = "$HISTCMD_previous" ]]; then
    # cancelled prompt
  else
    # a command was run
  fi
  HISTCMD_before_last=$HISTCMD_previous
}
PROMPT_COMMAND='prompt_command'

Note that if you've turned on squashing of duplicates in the history (HISTCONTROL=ignoredups or HISTCONTROL=erasedups), this will mistakenly report an empty command after running two identical commands successively.

  • Thanks, Gilles. I'm missing something here. This never seems to run, as putting 'echo hello' on the first line of the literal function doesn't work, though PROMPT_COMMAND='echo hello' does. I thought it might be the HISTCMD_previous vs HISTCMD_PREVIOUS issue, but no dice. I'll keep poking, but I'm commenting as your bash fu is clearly leagues beyond mine. – user Sep 03 '15 at 21:20
  • @user I fixed more typos, in particular the ${HISTCMD_previous%%$'[\t ]'*} bit was missing the $'…' and ended up truncating after \, t or space instead of after tab or space, but bash prints a tab. – Gilles 'SO- stop being evil' Sep 04 '15 at 07:40
  • 1
    This solution is based on the assumption that duplicates are saved in the history (which is OFF for me). Hence this solution does not work as expected for repeated commands while duplicates are not saved in the history. – schlimmchen May 11 '17 at 08:23
4

There is a workaround, but it has some requirements:

You need to set $HISTCONTROL to save ALL commands, also duplicates and spaces. So set:

HISTCONTROL=

Now define a function to call as $PROMPT_COMMAND:

isnewline () {
  # read the last history number
  prompt_command__isnewline__last="$prompt_command__isnewline__curr"
  # get the current history number
  prompt_command__isnewline__curr="$(history 1 | grep -oP '^\ +\K[0-9]+')"
  [ "$prompt_command__isnewline__curr" = "$prompt_command__isnewline__last" ] && \
    echo "User hit return"
}

Now, set the $PROMPT_COMMAND variable:

PROMPT_COMMAND="isnewline"

See the output:

user@host:~$ true
user@host:~$ <return>
User hit return
user@host:~$ <space><return>
user@host:~$ 
chaos
  • 48,171
  • I don't understand why you're using a temporary file here. The variable last is preserved from one invocation of isnewline to the next (only pick a less generic name like prompt_command__isnewline__last to avoid clashes). – Gilles 'SO- stop being evil' Sep 02 '15 at 20:53
  • @Gilles You're right, I changed it, thanks for your suggestion – chaos Sep 02 '15 at 21:07
  • Thanks, chaos. I used the same idea for the following, which is a bit easier to parse.

    HISTCONTROL="" function last_was_blank { local last_command="$(history 1)" if [[ "$last_was_blank_PREVIOUS_LINE" = "$last_command" ]] ; then echo "true" else echo "false" fi export last_was_blank_PREVIOUS_LINE="$last_command" } PROMPT_COMMAND=last_was_blank

    – user Sep 03 '15 at 21:26
1

I don’t know of a way to do that, per se.  But you can get the same effect by using

trap some_command_or_function debug

This will causes the some_command_or_function to be called any time you run a command.  The tricky thing is, it will not be called if you just hit Enter — unless you have a PROMPT_COMMAND defined, in which case hitting Enter invokes the PROMPT_COMMAND, which, in turn, triggers the trap.

Perhaps the simplest way to achieve the result that you want is to define a debug trap function instead of using a PROMPT_COMMAND.  But I can’t tell, because I don’t know what result you want.  If you want something to happen when you just hit Enter, and something different/additional to happen when you type a command, then (AFAIK) you need to use a debug trap and a PROMPT_COMMAND.  See this answer and this one for a way to make the two mechanisms play together nicely.

0

(This would have been a comment to the accepted answer if I had been allowed to add comments...) @schlimmen, you may set HISTTIMEFORMAT to something like HISTTIMEFORMAT='%F %T ' and then save and compare history 1. It's because with erasedups at least the timestamp of the (possibly repeated) last command changes every time --- and with HISSTIMEFORMAT appropriately set, history 1 will display the timestamp (unlike fc), and thus differ even between the repeated commands.