23

History is a shell-built in command I couldn't able to use that within a BASH script. So, Is there a way attain this using BASH script ?
Here we go my script for you:

#!/bin/bash
history |  tail -100 > /tmp/history.log
cd /tmp
uuencode history.log history.txt  | mail -s "History log of server" [email protected]

8 Answers 8

41

Bash disables history in noninteractive shells by default, but you can turn it on.

#!/bin/bash
HISTFILE=~/.bash_history
set -o history
history | tail …

But if you're trying to monitor activity on that server, the shell history is useless (it's trivial to run commands that don't show up in the history). See How can I log all process launches in Linux.

If you're debugging a script then shell history is not the best way to get useful information. A much better tool is the debug trace facility: put set -x near the top of the script. The trace is written to standard error.

7
  • This refuses to work in a script: histtest.sh: 5: set: Illegal option -o history
    – Ken Sharp
    Jan 7, 2016 at 2:56
  • 5
    @KenSharp That's because you tried to use it in an sh script (and your sh is dash juding by the exact wording of the error message). To access bash's history, you need to use bash (script starting with #!/bin/bash or #!/usr/bin/env bash). Jan 7, 2016 at 12:36
  • 1
    @KenSharp Yes it is. You obviously ran it under dash, given the error message. Bash does not have the error message Illegal option (it would say set: history: invalid option name). Jan 7, 2016 at 15:45
  • the problem with this is that it adds all commands following set -o history to the history which is then going to be stuffed up with entries like if [... vars=$(... etc.
    – nath
    Dec 18, 2017 at 2:42
  • 1
    @nath That's what the question asked for. It is pretty much pointless, as I point out. Monitoring activity is a hard problem that shell history won't solve. If you're debugging a script then set -x is a lot more useful. Dec 18, 2017 at 8:19
7

I'm not sure if it actually uses the history capability when running non-interactively, otherwise every shell script you run would clutter up your command history.

Why not go directly to the source ${HOME}/.bash_history, replace history | tail -100 with tail -100 ${HOME}/.bash_history. (If you use timestamps you'd probably have to do something along the lines of grep -v ^# ${HOME}/.bash_history | tail -100).

3
  • Indeed, with timestamps, you should actually greq -v '^#[0-9]\+$' (I sometimes "execute" comments in my shell...) Jan 18, 2018 at 12:17
  • And actually, in my case, I need to use the correct history line number, but there's a difference between the output of history and the output of the grep method... Jan 18, 2018 at 12:21
  • Ok, the fix was to synchronize the history list with the history file like this: history -a; history -c; history -r. (add, clear, read) Jan 18, 2018 at 12:33
3

If you want to use the output of the history command from an active shell session in a script, you can use an alias to run the command first. Then, in the same alias, you can call the remainder of the script. With such a configuration, you can achieve essentially the same result as having the history command in the actual script.

For instance, you can create an alias like this, assuming the script's name is script.sh:

alias hy_tmp='history | tail -100 > /tmp/history.log ; bash /patch/to/script.sh'

And change the script to this:

#!/bin/bash
cd /tmp
uuencode history.log history.txt  | mail -s "History log of server" [email protected]

I found this question while writing a process to combine, sort and synchronize ~/bash_history files on two computers so it'll be easy to search commands I've used in the past.

It's much less of a hassle to update my cumulative history file without having to log into a new shell to have ~/bash_history updated. For monitoring a server this will obviously not work, as mentioned in the other answers.

My usage in particular is:

alias hbye='history | cut -c 8- > /home/chris/.bash_history_c; bash /hby.sh

The script hby.sh then pulls all unique entries from all ~/.bash_history* files.

2

The history builtin seems to be disabled inside a shell script. See here: http://www.tldp.org/LDP/abs/html/histcommands.html

I have not found any official documentation about this.

1

Create a script named script.sh as below. It creates a script named X and puts Y Number of lines of your history into it.

#!/bin/bash
SCRIPT_NAME=$1
NUMBER_OF_LINES_BACK=$2

# Enable History in a non interactive shell
HISTFILE=~/.bash_history
set -o history

# echo shabang line and x number of lines of history to new script
echo \#\!\/bin\/bash > $SCRIPT_NAME.sh; history | tail -n $NUMBER_OF_LINES_BACK >> $SCRIPT_NAME.sh;
chmod u+x $SCRIPT_NAME.sh;

# Open the newly created script with vim
vim $SCRIPT_NAME.sh;
~

Then if you want to create a script to a accomplish a task named "task" that you have been working on for the last 14 lines run

script.sh task 14

Then clean up your history to make a nice script!

3
  • histtest.sh: 5: set: Illegal option -o history
    – Ken Sharp
    Jan 7, 2016 at 3:03
  • This is a really interesting idea, although the posted implementation has a fatal flaw; as soon as you set -o history, it starts appending all further commands in this script to your history. That means that when you actually try script.sh task 3 it will give you the same 3 commands from inside script.sh itself every time! The solution is of course to just tail the file directly, rather than calling set -o history. Jul 5, 2022 at 12:43
  • @Eric I've heavily modified your script until it finally works as expected (here you go). Try ls; date; scriptit lsdate 2 and see that vim opens with those two commands. However, not going to post it here as it doesn't actually answer this question. Jul 5, 2022 at 13:57
1

The history command is disabled by default on bash script that's why even history command won't work in .sh file. for its redirection, kindly redirect bash_history file inside the .sh file.

or history mechanism can be enabled also by mentioning history file and change run-time parameters as mentioned below

#!/bin/bash
HISTFILE=~/.bash_history
set -o history 

note: mentioned above two lines on the top of the script file. now history command will work in history.

0

Every user have it's own history file which is result of history command.Instead of using history command in your shell script you can use history file for user. There will be a .bash_history file in home directory of user, that will be the history file for the user.

-1

Short answer:

use history -p to perform history expansion explicitly.

An example follows.

One use case happens a lot for me is:

  • previous command get a list of file names, and print to stdout,
  • I need to edit those file with vim.

Since my bash has been set -o histexpand, usually what I did is typing

vim $(!!)

to edit those files listed by previous command.

Sadly sometimes mistake happens, so I enable verify by shopt -s histverify.

Now I can verify the expansion, with cost of one extra Return key.

Today I want to wrap those details into a bash function since I use them a lot,

function vk() {
    set -o history && set -o histexpand;
    vim -i ~/.viminfok $($(history -p !!));
}

Here I use two nested command substitution, the inner one to expand !! to previous command, the outer one to execute it and get the output.

Now I can achieve the same effect, with only three key, and one of those is Return!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .