Wednesday, February 15, 2012

bash history collector, without external scripts



# history_logger() side effects:
#   sets global variable '_last_history_number_logged'
#     $HISTCMD special variable should actually provide this,
#     But it appears unavailable in functions or at PROMPT_COMMAND eval time.
#
function history_logger() {
  local last_exit_code=$?   # must be the first command we run in this function
  local last_history_line=$(history 1)
  local _match_me="^ ?([0-9]+)"

  # Only log if we are not root:
  if [ "$(id -u)" -ne 0 ]; then
    # only log if history number changes:
    if [[ ${last_history_line} =~ ${_match_me} ]] &&
         [[ ${BASH_REMATCH[1]} != $_last_history_number_logged ]] ; then
      _last_history_number_logged=${BASH_REMATCH[1]} ;
      echo "$(date)] ${HOSTNAME%%.*}[$$:S${WINDOW}:${last_exit_code}] ${PWD/~/\~} ${last_history_line}" >> ~/.shell.log;
    fi ;
  fi
}

# the plumbing that makes it work:
export PROMPT_COMMAND='history_logger'


A few explanatory comments, as inline comments aren't available:
• HISTCMD does not appear to be available at function call or PROMPT_COMMAND time,
   so we use the [[ history =~ regex && BASH_REMATCH ]]  to hack it
• the assignment to ${_match_me} is because regex matching doesn't work correctly otherwise.
• the shell out to $(date) seems unavoidable.
• gnu screen is setting $WINDOW
• ${PWD/~/\~} is an exercise left to the reader.


I've seen other folks try to do this –– the best I found folks doing involved calling other bash scripts from the PROMPT_COMMAND.

I hit the same problem (inside a PROMPT_COMMAND, $HISTNUM is always == 1).
My solution uses a regex to capture the last history number, and doesn't require calling a separate script or set of scripts.

Comments welcome.

#RSFtalks with Edward Snowden

What an intelligent, thoughtful individual. I find it difficult to forgive 44 for failing to pardon this patriot and instead pursuing him ...

Other Popular Posts: