10

When running some tests, I need to run a series of commands. It would be extremely useful to me, and save me a lot of time, if there was a way to do all of these things:

  • Run the command I need to run
  • Redirect all the output from the command to a specified file
  • Include the original command in the specified file
  • Print the output from the original command in the terminal

People have suggested using tee to me which does a great job of printing to terminal as well as sending to a file but doesn't include the original command. What I'd like to end up with is a file where the first line is the command I ran, and then below that is the output from the command.

Someone suggested this:

echo "ls -l" | xargs -I{} bash -c "echo >> output.file; eval {} >> output.file"

But this doesn't either print the output in the terminal or include the original command in the file.

I'd appreciate any ideas.

3
  • 2
    Almost duplicate of askubuntu.com/q/688498/295286 . Nov 28, 2017 at 9:54
  • I didn't find this in my search - not exactly the same, but would probably have led me to the answer.
    – shaneoh
    Nov 28, 2017 at 10:12
  • yep, and this is why I said almost the duplicate. But to be perfectly honest with my opinion, you could take almost any solution from there, and make use of it with piping to tee. Nov 28, 2017 at 10:16

3 Answers 3

15

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.

0
9

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
7

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

You must log in to answer this question.

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