How to save terminal history to a file from a bash file?
Solution 1:
Short Answer
Run the script with source
or .
:
source ./script_name.sh
or
. ./script_name.sh
The latter is slightly more compatible across different shells.
Long Answer
This question highlights an important point, which is that shell scripts are run in their own context. To see what this means, consider the following shell script:
#!/bin/bash
cd /
ls
If you run this, you will get an output something like this:
bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
But you will notice that after you run the script, you are still in whatever directory you were in before you ran it: the cd /
inside the script hasn't actually affected your session - it only affected the context which the script was running in, which is created for the script to run in, and destroyed once it returns.
The source
command will 'read and execute commands from the filename argument in the current shell context', so any commands like cd
inside it will affect your current session. If you were to run the script above by passing it to source
you would find that you end up inside the root directory after it ran.
In this case, the problem is that the history
command gives you the history for the current shell context; the shell context that your script runs in without using source
has no history, so it doesn't write anything to the output file. If you use source
it will execute in the correct context, and will work as expected.
N.B.: source
is a shell built-in, not a program per se - in Bash, source
is synonymous with .
, but in some shells only .
will work - I've used source
in this answer because it's easier to read than .
, but for maximum compatibility, .
should be used.
Solution 2:
First of all, note that your history is already in a file. If you're running bash, its name is usually ~/.bash_history
. More specifically, it is whatever you have set the variable HISTFILE
to. If you want to copy it to another file, just run cat "$HISTFILE" > hist.txt
Now, as to why the history
command doesn't work in a bash shell script, that's because scripts are run in a non-interactive child shell of your current shell session. Child shells don't inherit all of the parent's environment (so not all of the set variables), only the variables that have been exported. To illustrate, the script below will echo the value of the variable $var
:
#!/bin/bash
echo "$var"
Now, set $var
to something and run the script:
$ var="foo"
$ foo.sh
VAR:
Next, export the variable first:
$ var="foo"
$ export var
$ foo.sh
VAR: foo
As you can see, when the variable has been exported, it is available to child shells.
As I mentioned before, the history is stored in the file pointed to by the variable $HISTFILENAME
. Because that isn't exported by default, it is not set when running a script:
$ cat foo.sh
#!/bin/bash
echo "HISTFILE: $HISTFILE"
$ ./foo.sh
HISTFILE:
$ echo $HISTFILE
/home/terdon/.bash_history
As you can see in the example above, the variable HISTFILE
is set in my normal shell session, but is empty when running the script.
So, to get the history, you have a few options:
-
The the default
HISTFILE
value is$HOME/.bash_history
. If you haven't changed that, you can simply run this command in your script:cat "$HOME/.bash_history" > history
-
You can pass the
$HISTFILE
variable to your script andcat
that:#!/bin/bash cat "$1" > history
Save the above as
foo.sh
and run like this:./foo.sh "$HISTORY"
-
Make sure the variable is exported. Add this line to your
~/.bash_profile
(if it exists) or~/.profile
(if~/.bash_profile
doesn't exist) files:export HISTFILE
Then, log out and log back in again and you should be able to run
history > hist.txt
from a script as expected. This is becauseexport VAR
means "make $VAR available to child shells". In practical terms, this means that the value ofHISTFILE
will be inherited by the non-interactive shell you use to run your script.Now, while the
HISTFILE
will be set, it hasn't been read by the shell running the script. So, to get it to work, you'd need to read it withhistory -r
first. The whole script would look like this:$!/bin/bash history -r history > hist.txt
Alternatively, just export it manually before running the script:
$ export HISTFILE
But you'll still need to
history -r
in the script. You can
source
it as suggested by @p0llard's answer.