In bash: processing every command line without using the debug trap?

699 Views Asked by At

I have a complicated mechanism built into my bash environment that requires the execution of a couple scripts when the prompt is generated, but also when the user hits enter to begin processing a command. I'll give an oversimplified description:

The debug trap does this in a fairly limited way: it fires every time a statement is executed.

trap 'echo $BASH_COMMAND' DEBUG  # example

Unfortunately, this means that when I type this:

sleep 1; sleep 2; sleep 3

rather than processing a $BASH_COMMAND that contains the entire line, I get the three sleeps in three different traps. Worse yet:

sleep 1 | sleep 2 | sleep 3

fires all three as the pipe is set up - before sleep 1 even starts executing, the output might lead you to believe that sleep 3 is running.

I need a way to execute a script right at the beginning, processing the entire command, and I'd rather it not fire when the prompt command is run, but I can deal with that if I must.

1

There are 1 best solutions below

0
On

THERE'S A MAJOR PROBLEM WITH THIS SOLUTION. COMMANDS WITH PIPES (|) WILL FINISH EXECUTING THE TRAP, BUT BACKGROUNDING A PROCESS DURING THE TRAP WILL CAUSE THE PROCESSING OF THE COMMAND TO FREEZE - YOU'LL NEVER GET A PROMPT BACK WITHOUT HITTING ^C. THE TRAP COMPLETES, BUT $PROMPT_COMMAND NEVER RUNS. THIS PROBLEM PERSISTS EVEN IF YOU DISOWN THE PROCESS IMMEDIATELY AFTER BACKGROUNDING IT.

This wound up being a little more interesting than I expected:

LOGFILE=~/logfiles/$BASHPID

start_timer() {
  if [ ! -e $LOGFILE ]; then
    #You may have to adjust this to fit with your history output format:
    CMD=`history | tail -1 | tr -s " " | cut -f2-1000 -d" "`
    #timer2 keeps updating the status line with how long the cmd has been running
    timer2 -p "$PROMPT_BNW $CMD" -u -q & echo $! > $LOGFILE
  fi
}

stop_timer() {
  #Unfortunately, killing a process always prints that nasty confirmation line,
  #and you can't silence it by redirecting stdout and stderr to /dev/null, so you
  #have to disown the process before killing it. 
  disown `cat $LOGFILE`
  kill -9 `cat $LOGFILE`
  rm -f $LOGFILE
}

trap 'start_timer' DEBUG