Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

In zsh's history file, all commands are logged from all terminal sessions. Is there any way to log in which terminal session a command was run (i.e., a unique identifier for this instance of the shell?).

A possible use case for this would be to look at the history file and determine what was run before or after a given command, restricted to this precise session.

share|improve this question

1 Answer 1

If you alter the history file then you will make trouble for yourself!

However it is possible to log the commands to a separate file. You can change the action that is performed when you execute a line to include logging the current line, and then you can consult that file to get the information you desire.

Lets first define a function that can log the current line and then execute it:

function log_and_accept {
    echo "$TTY $BUFFER" >> /tmp/log
    zle accept-line
}

Now $TTY is an identifier for your current shell, but it is not the only one you might use. $TTY is a path to a /dev/pts/NUMBER file. The only problem here is that this number is always the next number available, so if you close one terminal and open another then you will reuse the same tty number.

If you don't like this, then it is also possible to get a path that includes the process id of the shell (process ids can also be reused, but this is less likely to hamper you - process ids are used by all processes, while ttys are only terminals). This can be easily done thanks to the /proc/self symlink, which always points to the process folder for the process that looks at it. So to use the process folder, the above function would be:

function log_and_accept {
    process=/proc/self
    echo "${process:A} $BUFFER" >> /tmp/log
    zle accept-line
}

Now, once you have decided on how you want to identify the line, the next part is to actually replace the default behaviour with your new function. The default behaviour we are replacing is the accept-line function, and if we look at the zle documentation, you will see that accept-line is bound to ^J and ^M.

To bind this function to those letters you first need to turn it into a widget:

zle -N log_and_accept_widget log_and_accept

Then you can bind it, replacing the old behaviour:

bindkey '^J' log_and_accept_widget
bindkey '^M' log_and_accept_widget

Now zsh has multi line support, so when you consult this file you may well find that the lines end up being less clear than you originally hoped. Here is some of the output from the file I generated while testing this out:

/proc/19170 function log_and_accept {
shell=/proc/self
echo "${shell:A} $BUFFER" >> /tmp/log
zle accept-line
}
/proc/19170 echo woo
/proc/19170 cat /tmp/log

You may well want to pass the $BUFFER through a pretty printer of some kind before logging it.

If you run into problems doing this and you just really really want the old behaviour back then you can restore it with:

bindkey '^J' accept-line
bindkey '^M' accept-line

Although actually executing those commands might be ... difficult.

share|improve this answer
    
Thanks a lot for this detailed answer. I ended up changing the zshaddhistory() function to save history lines in a custom format in a custom file as well as preparing them for the standard log file. Details: gitorious.org/myconfig/myconfig/blobs/master/zsh/zshrc#line191. If you're wondering about the motivation: a3nm.net/blog/ttyrex.html. –  a3nm Jan 31 '13 at 20:13
    
Sounds pretty neat. I was using ttyrec a while ago for a presentation but I wasn't satisfied with the available ways to embed it. –  Matthew Franglen Jan 31 '13 at 21:23

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.