Saturday, February 13, 2016

save bash history remotely by sending a signal to all interactive bash

my bash history is an important resource and the primary way i record how i've solved problems. i use a unique file for each machine and store them in version control periodically

a problem that i run into every few months is i've solved a problem on one machine, but haven't yet exited from the shell so the solution is not stored in the history file yet. and i'm at a remote location, so i can't run "history -a"


add the following to .bashrc

# safely append the bash history function .save_hist { exec 200<${HISTFILE} if flock -w 5 200; then history -a flock -u 200 echo "SIGHUP received, history file saved" else echo "SIGHUP received, but lock failed, history not saved" fi exec 200<&- } trap '.save_hist' HUP # signal all bashes function save_hist { # not no tty and user and bash (ps h -o pid -Nt - | sed "s/ //g"; pgrep -u "$USER" bash) | sort | uniq -d | xargs kill -HUP }
to use it, ssh into the machine and run "save_hist"
Note: this is semi untested and has the potential to kill background process (the ps arguments aren't documented perfectly). but it appears to work
Note: *all* shells to have been started since the bashrc change to work

how it works

i wanted to use SIGUSR1, but bash doesn't appear to trap that signal until the user presses enter. which defeats the purpose. SIGHUP isn't really intuitive, but it is used by some daemons to reload their config file, which is somewhat along the same lines. and afaict, interactive bash doesn't use SIGHUP for anything. i'm open to other suggestions

trap is used to catch the HUP signal and then run "history -a". i use flock since it's not clear that bash handles simultaneous saves safely, and i routinely have dozens of bash tabs open.

you can use kill directly to send the signal, but i wanted to be able to save all the active interactive shells. i couldn't find an easy way to list them, so i came up with listing everything with a tty, combining that list with the users bash instances, filtering for only duplicates, and passing those PIDs to kill. it seems to work

good luck, and let me know if you have any problems


i posted this on [/r/bash]( and got downvoted as usual. but [lihaarp]( pointed out that you can set PROMPT_COMMAND. this saves the history after every command. i prefer my approach as it saves each bash instance in order, but it does mean you risk losing history if the computer crashes. ymmv

No comments: