In the bash reference manual it states:
lithist
If enabled, and thecmdhist
option is enabled, multi-line
commands are saved to the history with embedded newlines rather than
using semicolon separators where possible.
with this hence my question: where/when/how is this possible?
I tried to enable and test the feature on my GNU bash, version 4.4.12(1)-release doing this:
shopts -s cmdhist
shopts -s lithist
echo -n "this is a test for a ";\
echo "multiline command"
I then did a
history | tail
expecting some ouput akin to this :
101 shopts -s cmdlist
102 shopts -s lithist
103 echo -n "this is a test for a ";\
echo "multiline command"
104 history
yet instead get this:
101 shopts -s cmdlist
102 shopts -s lithist
103 echo -n "this is a test for a "; echo "multiline command"
104 history
As is obvious the multiline command (the one with bash history number 103) has not been stored with "embedded newlines rathar than using semicolon separators". Why was lithist
not possible here? What did I do wrong?
Best Answer
A
\<new line>
is not the correct way to get a<new line>
in the history.Memory
Lets deal only with history lines as they are kept in shell memory (not disk).
Lets type a couple of commands as you did:
What was stored in memory as the line just written?
As you can see, the "line continuation", a backslash followed by a newline, was removed.
As it should (from man bash):
We may get a newline if we quote it:
And, at this point, the history will reflect that:
A true multi-line command:
One possible example of a multi-line command is:
Which will be collapsed into one history line if
cmdhist is set
:The numbers for each command changed because at some point I cleared the history (in memory) with a
history -c
.If you unset the cmdhist, you will get this:
Each line (not a full command) will be at a separate line in the history.
Even if the
lithist
is set:But if both are set:
The
for
command was stored as a multiline command with "newlines" instead of semicolons (;
). Compare with above where lithist wasn't set.Disk
All the above was explained using the list of commands kept in the memory of the shell. No commands were written to the disk. The (default) file
~/.bash_history
was not changed.That file will be changed when the running shell exits. At that point in time the history will overwrite the file (if
histappend
isn't set), or will be appended otherwise.If you want the commands to be committed to disk you need to have this set:
That will make each command line to be appended to file on each new command line.
Now, lets get down to business with cmdhist and lithist. It is not so simple as it may seem. But don't worry, all will be clear in a moment.
Let's say that you take the time to type all the commands below (there is no shortcut, no alias, no function, you need the actual commands, sorry).
To first clear all history in memory (
history -c
) and in disk (make a backup) (history -w
) and then to try three times:List of commands to execute:
You will end with this (in memory):
The three multiline commands end as follows:
Ok, but what happen in file? say it alredy ....
finally, in file:
Simple, write to file and cat it to see this:
No line numbers, only commands, there is no way to tell where a multiline starts and where it ends. There is no way to tell even if there is a multiline.
In fact, that is exactly what happens, if the commands are written to file as above, when the file is read back, any information about multilines gets lost.
There is only one delimiter (the newline), each lines is read back as one command.
Is there a solution to this, yes, to use an additional delimter.
The HISTTIMEFORMAT kind of does that.
HISTTIMEFORMAT
When this variable is set to some value, the time at which each command was executed gets stored in file as the seconds since epoch (yes, always seconds) after a comment (
#
) character.If we set the variable and re-write the
~/.bash_history
file, we get this:Now you can tell where and which line is a multiline.
The format
'%FT%T '
shows the time but only when using the history command: