4

Let's say I issue some commands on the command line:

#capture what follows
$ echo "foo"
foo

# don't capture
$ echo "bing"
bing

# capture again
$ echo "bar"
bar

How can I log commands selectively to a logfile, which only captures the commands themselves issued on the cli? I.e. effectively achieving something similar to .bash_history, but only for certain commands:

$ cat command.log
echo "foo"
echo "bar"

Note that ouput to STDOUT of each command should not be logged.
I've looked at IO redirection, but could not figure out a working solution.

  • You say "selectively". What is the selection criteria? Are you fine with manually typing a few extra characters for every command you want to log? – jw013 Nov 21 '14 at 16:42
  • yes, that would be fine. – jottr Nov 21 '14 at 16:45
  • @jottr, you could try something as suggested here. – Ramesh Nov 21 '14 at 16:48
  • 1
    Why not set HISTFILE to your log for the session, effectively logging the commands to that file? – muru Nov 21 '14 at 16:55
  • 1
    @jottr The least clunky method I can come up with at the moment is to make an alias like alias loglast='fc -ln -1 | cut -c 3- > ~/command.log', and then just run loglast after every command you want to log. – jw013 Nov 21 '14 at 17:06
4

If you define a function like

loglast() {
    fc -ln -1 | sed 's/^[[:space:]]*//' >> "${1:-${logfile:-~/command.log}}"
}

then after every command you want to log, you can run loglast to log the previous command.

The log file used is (in order): the optional first argument to loglast, or $logfile if no argument given, or $HOME/command.log as a last default.

The sed -s '/^[[:space:]]*// removes the leading spaces that fc adds.

  • Easily support a selection argument like fc itself: loglast () { fc -ln ${1:--1} ${2:--1} ... which defaults to the one most recent command. To log only the 3rd most recent command, for example: loglast -3 -3 Bind it to a keystroke by adding this to your ~/.inputrc file: "\e\C-l": "loglast\n" which will allow you to log the most recent command by simply pressing Ctrl-Alt-l (the letter ell). Using the .inputrc file, the loglast command itself will be logged in Bash history when the keystroke is used. To avoid that, use bind -x '"\e\C-l": "loglast"' in your ~/.bashrc instead. – Dennis Williamson Nov 21 '14 at 23:42
  • Neat. Thx @DennisWilliamson – jottr Nov 22 '14 at 19:58
  • The HISTIGNORE approach is super simple. But the reason I like this answer is because you can retroactively log a command. Very handy if you're running commands and not sure of a successful outcome. – Snowcrash Aug 30 '17 at 10:20
  • This is helpful way than HISTCONTROL, for host where developers don't have home directory like production hosts. – mtk Oct 10 '17 at 13:48
7

The simplest approach is to use the functionality already provided by bash. Specifically, the HISTIGNORE variable:

   HISTCONTROL
          A  colon-separated  list  of values controlling how commands are
          saved on the history list.   If  the  list  of  values  includes
          ignorespace,  lines  which  begin with a space character are not
          saved in the history list. 

So, you could do something as simple as

$ HISTCONTROL=ignorespace

Then, any commands you enter with a leading space will be ignored:

HISTCONTROL=ignorespace
$ history -c            ## clear previous history for this session
$ echo foo
foo
$   echo bar
bar
$ history 
1  echo foo
2  history 

As you can see above, the command that started with a space was ignored.


You could also use HISTIGNORE:

    HISTIGNORE
          A colon-separated list of patterns used to decide which  command
          lines  should  be  saved  on  the history list.  Each pattern is
          anchored at the beginning of the line and must  match  the  com‐
          plete  line  (no  implicit  `*'  is  appended).  Each pattern is
          tested against the line after the checks specified  by  HISTCON‐
          TROL  are  applied.   In  addition  to  the normal shell pattern
          matching characters, `&' matches the previous history line.  `&'
          may  be  escaped  using  a  backslash;  the backslash is removed
          before attempting a match.  The second and subsequent lines of a
          multi-line compound command are not tested, and are added to the
          history regardless of the value of HISTIGNORE.

If you set HISTIGNORE to something like #foo and then append that to commands you want ignored, you can get the same effect:

$ HISTIGNORE="*#foo"
$ history -c  
$ echo foo
foo
$ echo "bar" #foo
bar
$ history 
1  echo foo
2  history 

In both cases, if you want to save this to a file, just run history > file. Alternatively, set the history file to file for the session:

$ HISTFILE="/tmp/file"
$ HISTCONTROL=ignorespace
$ history -c
$ echo foo
foo
$   echo bar
bar
$ history -a   ## write the session's history to $HISTFILE
$ cat /tmp/file 
echo foo
history -a
1

Use the script command:

script -f filename.log

It starts a new session and logs all your commands. When you exit the file is closed.

0

I could think of script command to log the commands. But this has the disadvantage of logging everything that you got in the standard output as well.

script -a #Start your scripting session. 

Script started, file is typescript
echo "Hello"
Hello
echo "Another Hello"
Another Hello
#Press Ctrl - D to exit the scripting session. 

Script done, file is typescript

Now, whatever you did in your session is logged in the typescript file.

cat typescript
Script started on Fri 21 Nov 2014 10:12:56 AM CST
echo "Hello"
Hello
echo "Another Hello"
Another Hello

Script done on Fri 21 Nov 2014 10:13:08 AM CST

EDIT

However, since you need to log only the commands rather than the output of the commands you could do something as suggested here.

  1. If you do not have screen, install it using apt-get install screen.
  2. Now, add the below contents to the file ~/.screenrc (Even if the file doesn't exist you can create it).

    screen -t "window 0" 0 bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
    screen -t "window 1" 1 bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
    screen -t "window 2" bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
    bind c screen bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
    bind ^C screen bash -ic 'HISTFILE=~/.bash_history.${WINDOW} bash'
    
  3. Now, start your session by typing screen command and execute your commands as you normally would.

  4. Now, you could view the list of the commands you used in the screen by checking the file ~/.bash_history.${WINDOW} where ${WINDOW} corresponds to the screen number in which you executed your commands.
  • Thx @Ramesh, but output should not be logged. – jottr Nov 21 '14 at 16:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.