Direct output from a command to a file including the original command, AND print in terminal

That's tee you're searching for.

ls -l | tee outfile

prints the output of ls -l to stdout (i.e. the terminal) and saves it in the file outfile at the same time. But: It doesn't write the command name neither to stdout nor to the file. To achieve that, just echo the command name before running the command and pipe both outputs to tee:

( echo "ls -l" && ls -l ) | tee outfile

That's cumbersome to type, so why not define a function?

both(){ ( echo "$@" && "$@" ) | tee outfile ;}

After that you can just run

both ls -l

to get the desired result. Put the function in your ~/.bashrc to have it defined in every new terminal.

If you want to be able to specify the output file as the first argument like in

both output ls -l

instead make it:

both(){ ( echo "${@:2}" && "${@:2}" ) | tee "$1" ;}

If you don't want the output file to be overwritten but rather append to it, add the -a option to tee.


You could make use of the script command which will make a typescript file of everything printed to your terminal. It creates a forked shells and will record everything until that shell is exited.

$ script my_output
Script started on Tue 28 Nov 2017 09:46:15 AM UTC
$ whoami
ajefferiss
$ exit
Script done on Tue 28 Nov 2017 09:46:27 AM UTC

Then if I cat my_output I get the same output:

$ cat my_output
Script started on Tue 28 Nov 2017 09:46:15 AM UTC
$ whoami
ajefferiss
$ exit
exit

Script done on Tue 28 Nov 2017 09:46:27 AM UTC

You can use the debugging function of the shell together with tee:

( set -x; command1 args...; command2 args ) 2>&1 | tee output.log
  • ( ... ) starts a sub-shell which allows you to “collect” the output streams of all commands executed within the sub-shell. It also contains the effect of the set command below to this sub-shell.

  • set -x enables the x shell option which prints all commands that the shell runs to the standard error stream before running them.

  • 2>&1 redirects stream 2 (standard error) to stream 1 (standard output).

  • | redirects the the standard output stream of the left command to the standard input stream of the right command.

  • tee FILE copies the standard input stream to the file FILE and to standard output.

If your command sequence is already in a script file it would make more sense to run it like this:

bash -x /path/to/script args... 2>&1 | tee output.log

Tags:

Command Line