0

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.

Jolta
  • 2,620
  • 1
  • 29
  • 42
Sniggerfardimungus
  • 11,583
  • 10
  • 52
  • 97
  • You want to have your code execute before the line is parsed and executed? What do you want to do at that point? – Etan Reisner Dec 03 '14 at 18:01
  • I have a couple things that I do with the commandline. I print it to the status line so the title of the xterm changes while the command is running. It makes finding a particular minimized window much easier. I also want to add a clock to the status line so I know how long a command has been running. It's easier than it sounds. The main thing is I want the entire command to be processed by my scripts. The simplest example I can construct is: trap 'echo -en "\033]0;$BASH_COMMAND\007"' DEBUG – Sniggerfardimungus Dec 03 '14 at 18:57
  • I can't think of any way to do this with bash (but newer versions might have something). This might be possible with zsh (since it tends to be a bit fancier in this sort of area) but I don't know. – Etan Reisner Dec 03 '14 at 19:32
  • I'm considering sticking with the use of the debug trap and doing this: When the trap fires, it checks to see if a certain file ($LOCK?) exists, and it doesn't, it creates it and does all the bookkeeping tasks (getting the command from the history appears to work). If $LOCK already exists, then the bookkeeping was already done for this command by an earlier trap. All I have to do is remove the file when the prompt is generated or when the user logs out, etc. – Sniggerfardimungus Dec 03 '14 at 19:55

1 Answers1

0

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
Sniggerfardimungus
  • 11,583
  • 10
  • 52
  • 97