HIST_IGNORE_ALL_DUPS
will throw out all previous matches of the command, which can be confusing when using the history as a log of what you did later:
HIST_IGNORE_ALL_DUPS:
If a new command line being added to the history list duplicates an older one, the older command is removed from the list (even if it is not the previous event).
A closer fit to your needs is probably the HIST_FIND_NO_DUPS
option:
HIST_FIND_NO_DUPS:
When searching for history entries in the line editor, do not display duplicates of a line previously found, even if the duplicates are not contiguous.
Or maybe the HIST_IGNORE_DUPS
, but, as RichieHH notes, it also leads to an incomplete account of history:
HIST_IGNORE_DUPS:
Do not enter command lines into the history list if they are duplicates of the previous event.
See: man zshoptions | less -p History
.
You need a way to find your processes which have been running for a while. To do that, it helps to know that the etime parameter shows the elapsed time since the process started, in the format DD-hh:mm:ss, where each bigger part is optional. So, you can do
ps -U sauer -o etime,pid,command
(on AIX, you could use ps -U sauer -o "%t,%p,%c"
)
You could use a -t $( tty)
in place of the -U username
to select processes on the current tty (aka, in the current terminal). That's what I'll do later. Also, note that doing -o cmd=
suppresses teh column header, but means you need multiple -o options.
So you end up with ps -t $(tty) -o etime= -o pid= -o command=
to show all the processes running on the current terminal, showing columns "elapsed wall clock time", "process ID", and "command", and suppressing the column headers.
Now you need to get only the ones which have been running for longer than whatever seconds. So, you need to extract the first column and convert the time to seconds. How about a shell script to do that (ksh/bash):
ps -t $(tty) -o etime= -o pid= -o command= | while read time pid cmd
do
secs=0
while true
do
part=${time##*:}
secs=$(( secs + ${part#0} ))
[[ "$part" != "$time" ]] || break # only had seconds
time=${time%:$part}
part=${time##*:}
secs=$(( secs + (60 * ${part#0}) ))
[[ "$part" != "$time" ]] || break # only had minutes left
time=${time%:$part}
part=${time##*-}
secs=$(( secs + (60 * 60 * ${part#0}) ))
[[ "$part" != "$time" ]] || break # only had hours left
time=${time%-$part} # all that's left are days-hours
secs=$(( secs + (24 * 60 * 60 * time) ))
break
done
echo "Process '$cmd' (pid '$pid') has been up for '$secs' seconds"
done
A couple of things may need explained. The ${part#0}
is in there so that times like "06" seconds are converted to "6" instead of being treated as octal. Sure, octal 06 and decimal 6 are the same, but octal 09 isn't valid, and shells sometimes whine about that.
The other thing is that ${var##*:}
evaluates to whatever $var is with the largest string matching "*:" pruned from the front (one #
is the shortest match, and %%
/ %
does the same from the end). If the pattern doesn't match, it evaluates to $var
. So, we yank off everything but the seconds, add that to seconds, then remove the seconds from the end. If we have stuff left, that's minutes. So, yank the minutes off, convert to seconds, add to the running total, prune it from the end. And so forth.
Alright. From there, now you just need to put a #!/bin/ksh
(or bash, if you have to) at the top, and you have a script. Except you also need a way to find just the pids that are running for more than X seconds. How about a file in /tmp. Let's use this in place of the echo command:
if [[ $secs -gt 120 ]]
then
{ cat /tmp/pids.$$; echo "$pid $cmd"; } 2>/dev/null | sort -u > /tmp/pids.$$.tmp
while read p c
do
if ps -p $p >/dev/null 2>&1
then
# put it back in the list
echo "$p $c" > /tmp/pids.$$
else
echo "Command '$c' exited" | mailx -s "exit report" $USER
fi
done < /tmp/pids.$$.tmp
fi
Now all you have to do is wrap the whole giant thing inside a while loop
while true
do
# the big while loop and the if statement
sleep 10
done
Call it process_monitor.sh
and stick a process_monitor.sh&
in your .profile or .bash_login or whatever. That'll sit in the background watching all the processes in that terminal, and when it finds one running for over 120 seconds, it'll stick that into the list to watch, and email you when the process exits.
There are things I'd probably add to make it more pretty, like trapping the EXIT signal and removing the temp files when the script exits, etc. But that's an exercise for you. I'm hoping this should be enough to get you well on the way. :)
Best Answer
Your prompt string probably has incorrectly escaped nonprinting characters. If you have anything like color changes in your prompt, you need to put
\[
and\]
around the terminal escape sequences so bash can tell that they don't take up space on screen — otherwise it miscalculates where the actual command is on screen, and updates it in the wrong place. See this section from TLDP. In zsh, put%{
and%}
around escape sequences.