Why Old Commands Show Incorrect Dates in Bash History After Enabling HISTTIMEFORMAT
Introduction
Bash keeps a per-user command history in ~/.bash_history. When HISTTIMEFORMAT
is set, bash also records and displays a timestamp for each command. A common
point of confusion arises when a history file contains a mix of entries: some
written before HISTTIMEFORMAT was ever set (no stored timestamp) and some
written after (with a stored timestamp). Under those conditions, old commands can
appear to have run "today," show up with unexpectedly high line numbers, and have
no matching login session. This looks alarming, but it is a display artifact, not
evidence that a command was just executed.
This article explains the behavior, how to confirm it, and how to stop the confusing output. The behavior is general to bash on any Linux distribution, including Rocky Linux.
Problem
An administrator reviewing root's command history sees a command they did not
expect, for example a grub2-mkconfig invocation, listed with the current date
and a high line number. There is no corresponding login in the login records, and
the same entry appears to advance to a new line number every time someone logs in.
The natural conclusion is that the command was run without authorization. In
reality, the entry is an old command with no stored timestamp being displayed
against the current time.
Symptoms
The following observations tend to appear together:
- A command shows the current date in
historyoutput, but the file it would have modified has a much older modification time. For example,historyreportsgrub2-mkconfigrunning today, whilels -lon the targetgrub.cfgshows a modification time weeks earlier. - The line number for a given entry keeps changing (advancing) on each login.
- No matching session appears in
lastfor the time the command supposedly ran. - No history-clearing or history-rewriting logic is present in
~/.bashrcor~/.bash_profile. - The history file contains two regions: an older block of plain command lines with no timestamps, followed by a block where each command is preceded by a comment line holding a Unix epoch value.
Inspecting the raw file shows the two regions directly:
# older region, written before HISTTIMEFORMAT was set (no timestamps)
cp /etc/default/grub /etc/default/grub-backup
vi /etc/default/grub
grub2-mkconfig -o /boot/grub2/grub.cfg
# newer region, written after HISTTIMEFORMAT was set (epoch timestamps)
#1690380695
vi /etc/sysctl.conf
#1690380736
Resolution
First, confirm the file has a mix of timestamped and un-timestamped entries by looking at the raw contents. The un-timestamped commands are the ones that will display incorrectly:
grep -n -B5 -A5 grub2-mkconfig ~/.bash_history
If you want to stop the confusing output, clear the history for the affected user. Back the file up first so you retain a record, then truncate it. This is the same pattern many sites already run against non-root accounts on a schedule:
cp -p ~/.bash_history ~/.bash_history.bak.$(date +%F)
: > ~/.bash_history
history -c
history -c clears the in-memory history for the current shell so the old entries
do not get written back out on logout. New commands recorded from this point will
carry a proper #<epoch> timestamp. Close any other shell sessions the user has
open before truncating; each open session holds its own in-memory copy of the old
history and can write it back to the file on exit, repopulating what you cleared.
To record timestamps consistently going forward, set HISTTIMEFORMAT in a shell
startup file. Adding it to ~/.bashrc covers that user's interactive shells; to
apply it for all users and shell types, add it under /etc/profile.d/ instead:
grep -q HISTTIMEFORMAT ~/.bashrc || echo 'export HISTTIMEFORMAT="%F %T "' >> ~/.bashrc
Note that this only affects how bash displays timestamps and whether it records them for new commands. It cannot retroactively assign a real time to entries that were written without one.
If your goal is to audit who ran a privileged command and when, do not rely on shell history. Use the audit daemon, which records the actual execution with a real timestamp and the login UID:
auditctl -w /usr/sbin/grub2-mkconfig -p x -k grub_command
ausearch -k grub_command --start today
The auditctl rule above applies to the running system only. To make it persist
across reboots, add the same rule to a file under /etc/audit/rules.d/ (for
example /etc/audit/rules.d/grub.rules) and reload with augenrules --load.
Root cause
Bash does not store a timestamp with a command unless HISTTIMEFORMAT is set at
the time the command is written to history. When it is set, bash writes a separate
line to ~/.bash_history consisting of # followed by the Unix epoch, immediately
before the command it applies to.
If HISTTIMEFORMAT was enabled at some point after the history file already
contained commands, those earlier commands have no stored #<epoch> line. When
history renders them, it has no real time to display, so it falls back to the
current time. That is why a legitimately old command appears to have run today.
Two more bash behaviors explain the rest of the symptoms. Bash does not sort
history by timestamp; it preserves the order in which entries were loaded and
recorded. When history from multiple shells is merged (for example via
history -n or history -r on login), older commands can end up interleaved and
carry higher line numbers than newer ones. And if the file is never cleared, it
keeps accumulating across every session, so the same old entry keeps getting
re-read and re-numbered on each login.
The takeaway is that shell history is a convenience feature, not an audit trail. It reflects what commands are stored in a file and how bash chooses to display them, not a reliable record of what ran and when.
Notes
Clearing history removes a troubleshooting aid, so keep the backup copy if you may
need to review past commands. For any real security or compliance requirement,
configure the audit daemon (auditd) with rules for the paths and commands you
care about. It records the actual system call, the real timestamp, and the login UID,
none of which shell history guarantees.
References & related articles
Bash Reference Manual: Bash History Facilities
Bash Reference Manual: Bash Variables (HISTTIMEFORMAT)