2

For a short period of time, I want to save the command history and output of every command I run in any terminal I open. It should all go in the same file.

So basically I want to run a command (or edit bashrc) such that I can then;

  • Open terminal #1 and run echo hello
  • Open terminal #2 and type ls
  • Close terminal #1 and open terminal #3 and type whoami
  • Close all terminals. Open terminal #4, and type a command to see all the commands run in order above (echo hello, ls, whoami), plus their output, in the order I ran the commands.

I could in theory open each terminal and type

screen -f output.txt

and then

exit

before I closed each terminal, but I don't want to have to remember to type that every time. I just want it done automatically until I manually stop it (by either running a command, or updating bashrc).

Please read the question carefully before declaring it a duplicate. I have searched existing questions and did not find something equivalent (though its possible I missed it).

muru
  • 197,895
  • 55
  • 485
  • 740
n00b
  • 155
  • 9
  • Possible duplicate: https://askubuntu.com/q/161935/295286 – Sergiy Kolodyazhnyy Dec 01 '18 at 18:43
  • @SergiyKolodyazhnyy that question asks about a single terminal window and the answer does not address how to do it automatically as I have asked for in this question. – n00b Dec 01 '18 at 18:44
  • 1
    Fair enough. I'll just point out a couple things: 1) automating such command could be done via ~/.bashrc , which gets sourced when interactive shell is open 2) script is as good as it gets when you want to log terminal / shell activity 3) merging multiple session records is going to be really difficult. So most reasonable solution is screen. What's being asked ( and if ever implemented ) would potentially be awkward and inefficient – Sergiy Kolodyazhnyy Dec 01 '18 at 18:51
  • You could create a new profile for the terminal that runs screen -f output.txt as the command, and set it as the default profile for the short while you want to save this output. – muru Dec 01 '18 at 19:40
  • I've made an attempt at a solution, although please see the Practical considerations part. Instead of dealing with 3 unrelated terminals, I'd suggest just dealing with one and keeping all necessary relevant information in one place. – Sergiy Kolodyazhnyy Dec 01 '18 at 21:56

1 Answers1

2

Potential solution

After some research and experimenting, here's what I came up with:

logstuff(){

    while true; do

        case $1 in
            "on" ) exec > >( ( printf ">>>>> TIME:$(date) SHELLPID:$$;\n"; tee /dev/tty ; printf ">>>>>\n" ) >> logfile.txt) 2>&1 ;
                   break;;
            "off") exec > /dev/tty 2>&1 ;
                   break;;
                *) echo "Please type 'on' or 'off';;
        esac
    done
}

This bash function should be placed in your ~/.bashrc and is available for use when opening new terminal or after issuing source ~/.bashrc. Logging has to be turned on manually via on and off arguments.


DEMO

Here's how it works in practice:

Do stuff in shell 1:

<shell 1>$ logstuff on
<shell 1>$ stat /etc/passwd
  File: /etc/passwd
  Size: 2208            Blocks: 8          IO Block: 4096   regular file
Device: 802h/2050d      Inode: 156236      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-12-01 20:24:02.620000000 +0000
Modify: 2018-10-31 01:33:42.701000999 +0000
Change: 2018-10-31 01:33:42.704998999 +0000
 Birth: -
<shell 1>$ logstuff off
<shell 1>$ 

Do stuff in shell 2:

< shell 2 >$ logstuff on
< shell 2 >$ echo "Hello World !"
Hello World !
< shell 2 >$ logstuff off
< shell 2 >$ 

Now review the logfile.txt:

<shell 1>$ cat logfile.txt 
>>>>> TIME:Sat Dec  1 21:43:00 UTC 2018 SHELLPID:2225;
<shell 1>$ stat /etc/passwd
  File: /etc/passwd
  Size: 2208            Blocks: 8          IO Block: 4096   regular file
Device: 802h/2050d      Inode: 156236      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-12-01 20:24:02.620000000 +0000
Modify: 2018-10-31 01:33:42.701000999 +0000
Change: 2018-10-31 01:33:42.704998999 +0000
 Birth: -
<shell 1>$ >>>>> TIME:Sat Dec  1 21:43:11 UTC 2018 SHELLPID:2359;
< shell 2 >$ echo "Hello World !"
Hello World !
< shell 2 >$ logstuff off
>>>>>
logstuff off
>>>>>
<shell 1>$ 

Issues

  • If logstuff on is issued in both terminals first, there's a chance of outputs being mangled together. The way it works is that you have to issue logsutff on in shell 1, then issue commands there, then issue logstuff on in shell 2.
  • This uses process substitution >( ), tee and a subshell. Not the most elegant nor efficient due to bunch of forking and extra pipeline.
  • logfile.txt is stored in current working directory. This should be changed to ~/logfile.txt or however the user sees fit.

Practical considerations

What the question itself asks is somewhat impractical: storing output from multiple shells into one single file means you're storing output of commands from two or more completely unrelated session, which may have different environment variables, different working directories, or working on different filesystems; this means there's whole lot of context lacking if you're intending to use such log text for debugging purposes or solving a problem.

Far better approach would be to have script -f write to log files in one specific directory, potentially with filenames of such logs timestamped or appended the shell PID. Another solution - instead of having 3 different terminals, just use one - screen or my personal favorite byobu-screen. You can attach/detach to a single virtual tty session in screen and it is often used to keep processes running on remote servers where you have to log out but still need a shell session with output and tracebacks running. This can be combined with script as well.

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497