Saving current directory in bash history

I want to keep the current directory where each team was released along with the team in history. In order not to mess things up, I thought about adding the current directory as a comment at the end of the line. An example might help:

$ cd /usr/local/wherever $ grep timmy accounts.txt 

I would like bash to save the last command as:

 grep timmy accounts.txt # /usr/local/wherever 

The idea is that in this way I could immediately see where I gave the command.

+58
bash
Jun 03 '09 at 15:01
source share
8 answers

Single Line Version

Here is the single line version. This is the original. I also published a short version of the function and a long version of the function with a few added functions. I like versions of functions because they will not absorb other variables in your environment, and they are much more readable than single-line ones. This post has some information on how they all work, which may not be duplicated in others.

Add the following to your ~/.bashrc :

 export PROMPT_COMMAND='hpwd=$(history 1); hpwd="${hpwd# *[0-9]* }"; if [[ ${hpwd%% *} == "cd" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd="${hpwd% ### *} ### $cwd"; history -s "$hpwd"' 

This makes a history entry that looks like this:

 rm subdir/file ### /some/dir 

I use ### as a comment separator to separate it from comments that the user can enter, and to reduce the likelihood of collisions when deleting old comments to paths that would otherwise accumulate when the enter key was pressed on an empty command line. Unfortunately, a side effect is that a command like echo " ### " distorted, although this should be pretty rare.

Some people find it unpleasant that I reuse the same variable name. Normally, I would not, but here I try to minimize the footprint. It has easily changed anyway.

This blindly assumes that you are not using HISTTIMEFORMAT or are modifying the story in any other way. It would be easy to add the date command to the comment instead of the HISTTIMEFORMAT function. However, if you need to use it for some reason, it still works in a subshell, as it automatically shuts down:

 $ htf="%Y-%m-%d %R " # save it for re-use $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25 

There are a couple of very small problems with this. One of them is if you use the history command, for example, like this:

 $ history 3 echo "hello world" ### /home/dennis ls -l /tmp/file ### /home/dennis history 3 

The result will not display a comment on the history command itself, even if you see it if you press the up arrow or enter another history command.

Another is that commands with inline newlines leave an uncommented copy in the history in addition to the commented out copy.

There may be other problems that are being discovered. Let me know if you find.

How does it work

Bash executes the command contained in the PROMPT_COMMAND variable each time a PS1 primary prompt is issued. This little script uses this feature to grab the last command in history, add a comment, and save it back.

Here it is shared with comments:

 hpwd=$(history 1) # grab the most recent command hpwd="${hpwd# *[0-9]* }" # strip off the history line number if [[ ${hpwd%% *} == "cd" ]] # if it a cd command, we want the old directory then # so the comment matches other commands "where *were* you when this was done?" cwd=$OLDPWD else cwd=$PWD fi hpwd="${hpwd% ### *} ### $cwd" # strip off the old ### comment if there was one so they # don't accumulate, then build the comment history -s "$hpwd" # replace the most recent command with itself plus the comment 
+47
Jun 04 '09 at 3:49
source share

hcmnt - version with long functions

Here is the long version as a function. This is a monster , but it adds some useful features. I also posted a single line (original) and shorter function. I like function versions because they will not clobber other variables in your environment and they are much more readable than single-line ones. For more information on how this works and some restrictions, read the entry for single line and total numbers in the function below. I posted each version in my own answer to make things more organized.

To use this, save it in a file called hcmnt in a place like /usr/local/bin (you can chmod +x it if you want), then enter it in your ~/.bashrc like this:

 source /usr/local/bin/hcmnt export hcmntextra='date "+%Y%m%d %R"' export PROMPT_COMMAND='hcmnt' 

Do not edit the function file where PROMPT_COMMAND or hcmntextra . Leave them such that they remain as default values. Add them to your .bashrc as shown above and edit them there to set options for hcmnt or to change or disable hcmntextra . Unlike a short function, with this you have to set the hcmntextra variable and use the -e parameter for this function to work.

In the comments to the function, you can add several parameters that are documented (with several examples). One notable feature is to have a history record with the added comment recorded in the file and leave the actual history untouched. To use this function, simply add the -l filename option as follows:

 export PROMPT_COMMAND="hcmnt -l ~/histlog" 

You can use any combination of options, except that -n and -t are mutually exclusive.

 #!/bin/bash hcmnt() { # adds comments to bash history entries (or logs them) # by Dennis Williamson - 2009-06-05 - updated 2009-06-19 # http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history # (thanks to Lajos Nagy for the idea) # the comments can include the directory # that was current when the command was issued # plus optionally, the date or other information # set the bash variable PROMPT_COMMAND to the name # of this function and include these options: # -e - add the output of an extra command contained in the hcmntextra variable # -i - add ip address of terminal that you are logged in *from* # if you're using screen, the screen number is shown # if you're directly logged in, the tty number or X display number is shown # -l - log the entry rather than replacing it in the history # -n - don't add the directory # -t - add the from and to directories for cd commands # -y - add the terminal device (tty) # text or a variable # Example result for PROMPT_COMMAND='hcmnt -et $LOGNAME' # when hcmntextra='date "+%Y%m%d %R"' # cd /usr/bin ### mike 20090605 14:34 /home/mike -> /usr/bin # Example for PROMPT_COMMAND='hcmnt' # cd /usr/bin ### /home/mike # Example for detailed logging: # when hcmntextra='date "+%Y%m%d %R"' # and PROMPT_COMMAND='hcmnt -eityl ~/.hcmnt.log $LOGNAME@$HOSTNAME' # $ tail -1 ~/.hcmnt.log # cd /var/log ### dave@hammerhead /dev/pts/3 192.168.1.1 20090617 16:12 /etc -> /var/log # INSTALLATION: source this file in your .bashrc # will not work if HISTTIMEFORMAT is used - use hcmntextra instead export HISTTIMEFORMAT= # HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically: # $ htf="%Y-%m-%d %R " # save it for re-use # $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25 local script=$FUNCNAME local hcmnt= local cwd= local extra= local text= local logfile= local options=":eil:nty" local option= OPTIND=1 local usage="Usage: $script [-e] [-i] [-l logfile] [-n|-t] [-y] [text]" local newline=$'\n' # used in workaround for bash history newline bug local histline= # used in workaround for bash history newline bug local ExtraOpt= local LogOpt= local NoneOpt= local ToOpt= local tty= local ip= # *** process options to set flags *** while getopts $options option do case $option in e ) ExtraOpt=1;; # include hcmntextra i ) ip="$(who --ips -m)" # include the terminal ip address ip=($ip) ip="${ip[4]}" if [[ -z $ip ]] then ip=$(tty) fi;; l ) LogOpt=1 # log the entry logfile=$OPTARG;; n ) if [[ $ToOpt ]] then echo "$script: can't include both -n and -t." echo $usage return 1 else NoneOpt=1 # don't include path fi;; t ) if [[ $NoneOpt ]] then echo "$script: can't include both -n and -t." echo $usage return 1 else ToOpt=1 # cd shows "from -> to" fi;; y ) tty=$(tty);; : ) echo "$script: missing filename: -$OPTARG." echo $usage return 1;; * ) echo "$script: invalid option: -$OPTARG." echo $usage return 1;; esac done text=($@) # arguments after the options are saved to add to the comment text="${text[*]:$OPTIND - 1:${#text[*]}}" # *** process the history entry *** hcmnt=$(history 1) # grab the most recent command # save history line number for workaround for bash history newline bug histline="${hcmnt% *}" hcmnt="${hcmnt# *[0-9]* }" # strip off the history line number if [[ -z $NoneOpt ]] # are we adding the directory? then if [[ ${hcmnt%% *} == "cd" ]] # if it a cd command, we want the old directory then # so the comment matches other commands "where *were* you when this was done?" if [[ $ToOpt ]] then cwd="$OLDPWD -> $PWD" # show "from -> to" for cd else cwd=$OLDPWD # just show "from" fi else cwd=$PWD # it not a cd, so just show where we are fi fi if [[ $ExtraOpt && $hcmntextra ]] # do we want a little something extra? then extra=$(eval "$hcmntextra") fi # strip off the old ### comment if there was one so they don't accumulate # then build the string (if text or extra aren't empty, add them plus a space) hcmnt="${hcmnt% ### *} ### ${text:+$text }${tty:+$tty }${ip:+$ip }${extra:+$extra }$cwd" if [[ $LogOpt ]] then # save the entry in a logfile echo "$hcmnt" >> $logfile || echo "$script: file error." ; return 1 else # workaround for bash history newline bug if [[ $hcmnt != ${hcmnt/$newline/} ]] # if there a newline in the command then history -d $histline # then delete the current command so it not duplicated fi # replace the history entry history -s "$hcmnt" fi } # END FUNCTION hcmnt # set a default (must use -e option to include it) export hcmntextra='date "+%Y%m%d %R"' # you must be really careful to get the quoting right # start using it export PROMPT_COMMAND='hcmnt' 

update 2009-06-19 : added options useful for logging (ip and tty), workaround for problems with duplicate entries, removal of extraneous null assignments

+17
Jun 06 '09 at 22:17
source share

You can install Advanced Shell History , an open source tool that writes bash or zsh history to sqlite database. It records things like the current working directory, command exit code, command start and stop times, session start and end times, tty, etc.

If you want to query the history database, you can write your own SQL queries, save them and make them available in the ash_query . There are some useful prepackaged queries, but since I know SQL well, I usually just open the database and query interactively when I need to look for something.

One query, which I find very useful, however, looks at the history of the current working directory. It helps me remember where I left off when I was working on something.

 vagrant@precise32:~$ ash_query -q CWD session when what 1 2014-08-27 17:13:07 ls -la 2014-08-27 17:13:09 cd .ash 2014-08-27 17:16:27 ls 2014-08-27 17:16:33 rm -rf advanced-shell-history/ 2014-08-27 17:16:35 ls 2014-08-27 17:16:37 less postinstall.sh 2014-08-27 17:16:57 sudo reboot -n 

And the same story using the current working directory (and something below):

 vagrant@precise32:~$ ash_query -q RCWD session where when what 1 /home/vagrant/advanced-shell-history 2014-08-27 17:11:34 nano ~/.bashrc 2014-08-27 17:12:54 source /usr/lib/advanced_shell_history/bash 2014-08-27 17:12:57 source /usr/lib/advanced_shell_history/bash 2014-08-27 17:13:05 cd /home/vagrant 2014-08-27 17:13:07 ls -la 2014-08-27 17:13:09 cd .ash /home/vagrant/.ash 2014-08-27 17:13:10 ls 2014-08-27 17:13:11 ls -l 2014-08-27 17:13:16 sqlite3 history.db 2014-08-27 17:13:43 ash_query 2014-08-27 17:13:50 ash_query -Q 2014-08-27 17:13:56 ash_query -q DEMO 2014-08-27 17:14:39 ash_query -q ME 2014-08-27 17:16:26 cd /home/vagrant 2014-08-27 17:16:27 ls 2014-08-27 17:16:33 rm -rf advanced-shell-history/ 2014-08-27 17:16:35 ls 2014-08-27 17:16:37 less postinstall.sh 2014-08-27 17:16:57 sudo reboot -n 

FWIW - I am the author and supporter of the project.

+13
Aug 30 '14 at 20:03
source share

hcmnts - short functional version

Here is the short version as a function. I also posted a one-line (original) and longer function with a few added functions. I like function versions because they will not clobber other variables in your environment, and they are much more readable than single-line ones. Read the entry for a single-line image for more information on how this works and what are the limitations. I posted each version in my own answer to make things more organized.

To use this, save it in a file called hcmnts in a place like /usr/local/bin (you can chmod +x it if you want), then enter it in your ~/.bashrc like this:

 source /usr/local/bin/hcmnts 

Comment on the line that hcmntextra sets if you do not need the date and time (or you can change its format or use a command other than date ).

That is all that is needed.

 #!/bin/bash hcmnts() { # adds comments to bash history entries # the *S*hort version of hcmnt (which has many more features) # by Dennis Williamson # http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history # (thanks to Lajos Nagy for the idea) # INSTALLATION: source this file in your .bashrc # will not work if HISTTIMEFORMAT is used - use hcmntextra instead export HISTTIMEFORMAT= # HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically: # $ htf="%Y-%m-%d %R " # save it for re-use # $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25 local hcmnt local cwd local extra hcmnt=$(history 1) hcmnt="${hcmnt# *[0-9]* }" if [[ ${hcmnt%% *} == "cd" ]] then cwd=$OLDPWD else cwd=$PWD fi extra=$(eval "$hcmntextra") hcmnt="${hcmnt% ### *}" hcmnt="$hcmnt ### ${extra:+$extra }$cwd" history -s "$hcmnt" } export hcmntextra='date +"%Y%m%d %R"' export PROMPT_COMMAND='hcmnts' 
+6
Jun 06 '09 at 22:12
source share

For those who want this in zsh, I have changed . Implementation Jeet Sukumaran and percol allow interactive keyword searches and retrieval of either the command or the path in which it was executed. You can also filter duplicate commands and hide fields (date, command, path)

+2
03 Oct '14 at 8:31
source share

Gentleman, this works better. The only thing I can’t understand is to make the script NOT log to syslog at login and write the last command to the history. But while it works like a charm.

 #! / bin / bash

 trackerbash () {
     # adds comments to bash history entries

     # by Dennis Williamson
     # http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
     # (thanks to Lajos Nagy for the idea)

     #Supper Enhanced by QXT


     # INSTALLATION: source this file in your .bashrc

     export HISTTIMEFORMAT =
 # export HISTTIMEFORMAT = '% F% T'

     local hcmnt
     local cwd
     local extra
     local thistty
     local whoiam
     local sudouser
     local shelldate
     local TRACKIP
     local TRACKHOST


             thistty = `/ usr / bin / tty | / bin / cut -f3-4 -d /`
             whoiam = `/ usr / bin / whoami`
             sudouser = `last | grep $ thistty | head -1 |  awk '{print $ 1}' | cut -c 1-10`
             hcmnt = $ (history 1)
             hcmnt = "$ {hcmnt # * [0-9] *}"
             cwd = `pwd`



             hcmnt = "$ {hcmnt% ### *}"
             hcmnt = "$ hcmnt $ {extra: + $ extra}"

             shelldate = `date +"% Y% b% d% R:% S "`
             TRACKHOST = `whoami |  sed -r "s /.* \ ((. *) \). * / \\ 1 /" `
             TRACKIP = `last | grep $ thistty | head -1 |  awk '{print $ 3}' '


             logger -p local1.notice -t bashtracker -i - "$ sudouser $ {USER}: $ thistty: $ TRACKIP: $ shelldate: $ cwd: $ hcmnt"
             history -w 

 }
 export PROMPT_COMMAND = 'trackerbash'

+1
Jun 28 '11 at 19:51
source share

Here is one liner of what I'm using. Paste it here because it is much easier and I have no problems with the session history, I just want to have a history with the working directory.

Also, the one-liner above spoils your user interface too much.

 export PROMPT_COMMAND='if [ "$(id -u)" -ne 0 ]; then echo "$(date "+%Y-%m-%d.%H:%M:%S") $(pwd) $(history 1)" >> ~/.bash.log; fi' 

Since my home directory is typically a crossover thing, this is a side effect - the story of everything I've ever done. Add $(hostname) to the echo command above ... if desired, depending on your working environment.

Grep is more than good even with 100k records. No need sqlite to log in. Just do not enter passwords on the command line, and everything is fine. Passwords anyway 90 technologies!

Also for search I tend to do this:

 function hh() { grep "$1" ~/.bash.log } 
0
Oct 18 '17 at 13:09 on
source share

Full disclosure: I am the author of the FOSS tool
shournal - the log (file-) for your shell :
Using bash integration, the working directory of the command is also stored in shournal sqlite-database and can be obtained through

 shournal --query -cmdcwd "$PWD" 

Queries for working subdirectories can be performed using

 shournal --query -cmdcwd -like "$PWD/%" 
0
Jul 23 '19 at 23:00
source share



All Articles