TrueNAS / FreeBSD / csh history command: custom date format

First, the solution. A long one-liner. Making use of Python being shipped with FreeNAS:

# cat .history | python -c 'from datetime import datetime as dt; import sys; lines=[l for l in sys.stdin]; chunks=[lines[i:i + 2] for i in range(0, len(lines), 2)]; [print(dt.fromtimestamp(int(time[2:])), cmd.strip()) for time, cmd in chunks]' | head -n5 
2019-09-15 22:31:36 ls
2019-09-15 22:31:41 gpart
2019-09-15 22:31:44 gpart list
2019-09-15 22:32:32 gpart list
2019-09-15 22:32:45 gpart list

A little more readable (not so much horizontal scrolling):

cat .history | python -c \
    'from datetime import datetime as dt;'
    'import sys; lines=[l for l in sys.stdin];'
    'chunks=[lines[i:i + 2] for i in range(0, len(lines), 2)];'
    '[print(dt.fromtimestamp(int(time[2:])), cmd.strip()) for time, cmd in chunks]' \
        | head -n5 

FreeBSD’s history command does not seem to have an interface for formatting the time each command was executed. I only get the time of the day. Example output line:

$ history
...
   556  17:49   find . -iname "*astronaut*"
...

That is of limited use when you have accumulated commands over the course of many years; year, month, and day can be rather useful information :-) aaaaah, this was 2019!

This mailing list thread seems to confirm that there is no interface for configuring the output time format.

So I decided to simply parse the ~/.history data file with Python. The history data file looks like that (showing the first four lines):

$ head -n4 < .history
#+1568579496
ls
#+1568579501
gpart

The ugly Python one-liner above iterates over text lines in stdin and groups them in pairs of two. Each pair is used for creating one line of text in the output.

I saved my full history with

# cat .history | python -c 'from datetime import datetime as dt; import sys; lines=[l for l in sys.stdin]; chunks=[lines[i:i + 2] for i in range(0, len(lines), 2)]; [print(dt.fromtimestamp(int(time[2:])), cmd.strip()) for time, cmd in chunks]' > freenas_history_root_230429.log

I think the reference documentation for the specific history command that I was working with here is

       history [-hTr] [n]
       history -S|-L|-M [filename] (+)
       history -c (+)
           The first form prints the history event list.  If  n  is  given
           only  the  n most recent events are printed or saved.  With -h,
           the history list is printed without leading numbers.  If -T  is
           specified,  timestamps are printed also in comment form.  (This
           can be used to produce files suitable for loading with 'history
           -L'  or  'source  -h'.)  With -r, the order of printing is most
           recent first rather than oldest first.

           With -S, the second form saves the history  list  to  filename.
           If  the  first  word of the savehist shell variable is set to a
           number, at most that many lines are saved.  If the second  word
           of  savehist is set to `merge', the history list is merged with
           the existing history file instead of replacing it (if there  is
           one)  and sorted by time stamp.  (+) Merging is intended for an
           environment like the X Window System with several shells in si-
           multaneous  use.  If the second word of savehist is `merge' and
           the third word is set to `lock', the history file  update  will
           be  serialized  with  other  shell sessions that would possibly
           like to merge history at exactly the same time.

           With -L, the shell appends filename, which is presumably a his-
           tory  list saved by the -S option or the savehist mechanism, to
           the history list.  -M is like -L, but the contents of  filename
           are  merged  into the history list and sorted by timestamp.  In
           either case, histfile is used if  filename  is  not  given  and
           ~/.history  is  used if histfile is unset.  `history -L' is ex-
           actly like 'source -h' except that it does not require a  file-
           name.

           Note  that  login  shells  do the equivalent of `history -L' on
           startup and, if savehist is set, `history -S'  before  exiting.
           Because  only  ~/.tcshrc is normally sourced before ~/.history,
           histfile should be set in ~/.tcshrc rather than ~/.login.

           If histlit is set, the first and second forms  print  and  save
           the literal (unexpanded) form of the history list.

           The last form clears the history list.

taken from FreeBSD 13’s csh man page.

There is no mention of e.g. HISTTIMEFORMAT or a corresponding command line argument.

Notes:
* In the output of the Python program I deliberately didn’t care to do timezone handling; the output is a local time probably in UTC or my local timezone, and both are fine.
* In the output you might see a BrokenPipeError — which is entirely expected when the process attached to stdin goes away.

Leave a Reply

Your email address will not be published. Required fields are marked *

Human? Please fill this out: * Time limit is exhausted. Please reload CAPTCHA.