Guardar el directorio actual en el historial de bash

Me gustaría guardar el directorio actual donde se emitió cada comando junto con el comando en el historial. Para no complicar las cosas, estaba pensando en agregar el directorio actual como un comentario al final de la línea. Un ejemplo podría ayudar:

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

Me gustaría que bash guarde el último comando como:

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

La idea es que de esta manera podría ver de inmediato dónde emití el comando.

Versión de una línea

Aquí hay una versión de una línea. Es el original. También publiqué una versión de función corta y una versión de función larga con varias funciones adicionales. Me gustan las versiones de funciones porque no afectarán a otras variables en su entorno y son mucho más legibles que las de una sola línea. Esta publicación contiene información sobre cómo funcionan todos los que no pueden duplicarse en los demás.

Agregue lo siguiente a su archivo ~/.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"' 

Esto hace una entrada de historia que se ve así:

 rm subdir/file ### /some/dir 

Utilizo ### como un delimitador de comentarios para diferenciarlo de los comentarios que el usuario podría escribir y para reducir las posibilidades de colisiones cuando elimino comentarios de ruta antiguos que de otro modo se acumularían si presiona enter en una línea de comando en blanco. Desafortunadamente, el efecto secundario es que un comando como echo " ### " se destroza, aunque eso debería ser bastante raro.

Algunas personas considerarán desagradable el hecho de que reutilice el mismo nombre de variable. Normalmente no lo haría, pero aquí estoy tratando de minimizar la huella. Se puede cambiar fácilmente en cualquier caso.

Asume ciegamente que no estás usando HISTTIMEFORMAT o modificando el historial de alguna otra manera. Sería fácil agregar un comando de date al comentario en lugar de la función HISTTIMEFORMAT . Sin embargo, si necesita usarlo por alguna razón, todavía funciona en una subcaja, ya que se desarma automáticamente:

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

Hay un par de problemas muy pequeños con eso. Una es si usa el comando history esta manera, por ejemplo:

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

El resultado no mostrará el comentario en el comando de history sí, aunque lo verá si presiona la flecha hacia arriba o si emite otro comando de history .

La otra es que los comandos con líneas nuevas incrustadas dejan una copia sin comentario en el historial además de la copia comentada.

Puede haber otros problemas que aparecen. Avísame si encuentras alguno.

Cómo funciona

Bash ejecuta un comando contenido en la variable PROMPT_COMMAND cada vez que se emite el mensaje primario de PS1 . Este pequeño script aprovecha eso para tomar el último comando en el historial, agregarle un comentario y guardarlo nuevamente.

Aquí se divide con comentarios:

 hpwd=$(history 1) # grab the most recent command hpwd="${hpwd# *[0-9]* }" # strip off the history line number if [[ ${hpwd%% *} == "cd" ]] # if it's 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 

hcmnt – versión de función larga

Aquí hay una versión larga en forma de una función. Es un monstruo , pero agrega varias funciones útiles. También publiqué una línea (la original) y una función más corta. Me gustan las versiones de funciones porque no afectarán a otras variables en su entorno y son mucho más legibles que las de una sola línea. Lea la entrada del documento de una línea y los comentarios en la función a continuación para obtener información adicional sobre cómo funciona y algunas limitaciones. He publicado cada versión en su propia respuesta para mantener las cosas más organizadas.

Para usar este, guárdelo en un archivo llamado hcmnt en una ubicación como /usr/local/bin (puede chmod +x si lo desea) y luego conéctelo en su ~/.bashrc siguiente manera:

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

No edite el archivo de la función donde se establecen PROMPT_COMMAND o hcmntextra . Déjelos tal como están, para que permanezcan como predeterminados. Inclúyalos en su .bashrc como se muestra arriba y edítelos allí para establecer opciones para hcmnt o para cambiar o desarmar hcmntextra . A diferencia de la función corta, con esta, ambos deben tener el hcmntextra variables hcmntextra y usar la opción -e para que esa característica funcione.

Puede agregar varias opciones que están documentadas (con un par de ejemplos) en los comentarios de la función. Una característica notable es tener la entrada del historial con el comentario adjunto registrado en un archivo y dejar el historial real intacto. Para usar esta función, simplemente agregue la opción -l nombre de archivo así:

 export PROMPT_COMMAND="hcmnt -l ~/histlog" 

Puede usar cualquier combinación de opciones, excepto que -n y -t son mutuamente excluyentes.

 #!/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's 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's 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's 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's 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' 

actualización 2009-06-19 : Se agregaron opciones útiles para el registro (ip y tty), una solución para el problema de entrada duplicada, se eliminaron las asignaciones nulas extrañas

Puede instalar Advanced Shell History , una herramienta de código abierto que escribe su historial bash o zsh en una base de datos sqlite. Esto registra cosas como el directorio de trabajo actual, el código de salida del comando, las horas de inicio y finalización del comando, las horas de inicio y finalización de la sesión, tty, etc.

Si desea consultar la base de datos del historial, puede escribir sus propias consultas SQL, guardarlas y ponerlas a disposición dentro de la herramienta ash_query incluida. Hay algunas consultas preempaquetadas útiles, pero como conozco bastante bien el SQL, suelo abrir la base de datos y consultar de forma interactiva cuando necesito buscar algo.

Sin embargo, una consulta que encuentro muy útil es ver el historial del directorio de trabajo actual. Me ayuda a recordar dónde lo dejé cuando estaba trabajando en algo.

 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 

Y la misma historia usando el directorio de trabajo actual (y cualquier cosa debajo de él):

 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: soy el autor y mantenedor del proyecto.

hcmnts – versión de función corta

Aquí hay una versión corta en forma de una función. También publiqué una línea única (la original) y una función más larga con varias funciones adicionales. Me gustan las versiones de funciones porque no afectarán a otras variables en su entorno y son mucho más legibles que las de una sola línea. Lea la entrada para el one-liner para obtener información adicional sobre cómo funciona esto y algunas limitaciones. He publicado cada versión en su propia respuesta para mantener las cosas más organizadas.

Para usar este, guárdelo en un archivo llamado hcmnts en una ubicación como /usr/local/bin (puede chmod +x si lo desea) y luego conéctelo en su ~/.bashrc siguiente manera:

 source /usr/local/bin/hcmnts 

Comente la línea que establece hcmntextra si no desea la fecha y la hora (o puede cambiar su formato o usar algún otro comando además de la date ).

Eso es todo al respecto.

 #!/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' 

Caballero esto funciona mejor … Lo único que no puedo entender es cómo hacer que la secuencia de comandos NO inicie sesión en syslog al iniciar sesión y registre el último comando en la historia. Pero funciona como un encanto hasta ahora.

 #! / bin / bash

 trackerbash () {
     # agrega comentarios a las entradas del historial de bash

     # por Dennis Williamson
     # http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
     # (gracias a Lajos Nagy por la idea)

     #Supper mejorado por QXT


     # INSTALACIÓN: fuente este archivo en su .bashrc

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

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


             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 = $ (historia 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"
             historia -w 

 }
 exportar PROMPT_COMMAND = 'trackerbash'

Para aquellos que quieren esto en zsh, he modificado la implementación y el percol de Jeet Sukumaran para permitir la búsqueda interactiva de palabras clave y la extracción del comando o la ruta en la que se ejecutó. También es posible filtrar comandos duplicados y ocultar campos (fecha, comando, camino)

Aquí hay un trazador de líneas de lo que uso. Ponerlo aquí porque es mucho más simple, y no tengo ningún problema con el historial por sesión, también quiero tener un historial con el directorio de trabajo.

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

Dado que mi dir de inicio es típicamente una cosilla de grusster montada en cruz, esto tiene el efecto secundario de ser un historial de todo lo que he hecho. Opcionalmente agregue $(hostname) al comando de eco de arriba … dependiendo de su entorno de trabajo.

Incluso con 100k entradas, grep es más que suficiente. No es necesario volver a iniciar sesión en sqlite. Simplemente no escriba contraseñas en la línea de comandos y está bien. ¡Las contraseñas son tecnología de los 90 de todos modos!

Además, para buscar, tiendo a hacer esto:

 function hh() { grep "$1" ~/.bash.log }