75

How to redirect standard output to multiple log files? The following does not work:

some_command 1> output_log_1 output_log_2 2>&1
2
  • 6
    With zsh, you can use some_command >output_log_1 >output_log_2.
    – jofel
    Jun 21, 2012 at 8:36
  • @jofel That's incorrect, check my answer below with example from zsh manual.
    – reportaman
    Oct 12, 2021 at 7:46

6 Answers 6

108

See man tee:

NAME: tee - read from standard input and write to standard output and files

SYNOPSIS: tee [OPTION]... [FILE]...

Accordingly:

echo test | tee file1 file2 file3
9
  • can the stderr be also redirected in more than one file?
    – fromnaboo
    Jun 23, 2012 at 9:07
  • Yes, it can be done by virtue of redirection: find / -name test 2>&1 | tee file1 file2 file3
    – akond
    Jun 23, 2012 at 18:27
  • @akond, cmd 2>&1 | tee log1 log2 I tried executing like above, but i need to press ctrl-c to redirect it to second log file. also the output is printed on the console. I want command output to be redirected to logs but not on the console. any help is appreciated. Jun 25, 2012 at 10:34
  • 1
    @doubledecker The tee command writes stdin to file(s) and also to stdout. If you don't want the output to appear on the terminal, you have to redirect to /dev/null like you normally would.
    – Minix
    Dec 16, 2014 at 8:28
  • 7
    It is also possible to append to multiple files: echo test | tee --append file1 file2 Jul 29, 2016 at 17:53
15

Let's say your output is generated from a function, cmd() :

cmd() {
    echo hello world!
}

To redirect the output from cmd to two files, but not to the console, you can use:

cmd | tee file1 file2 >/dev/null

This will work for multiple files, given any data source piping to tee:

echo "foobarbaz" | tee file1 file2 file3 file4 > /dev/null

This will also work:

echo $(cmd) | tee file1 file2 >/dev/null

Without the /dev/null redirection, tee will send output to stdout in addition to the files specified.

For example, if this is run from the console, you'll see the output there. Run from a crontab, the output will appear the status message which is mailed to you (also see Gilles' answer here https://unix.stackexchange.com/a/100833/3998).

This worked for me in bash on Ubuntu 12.04, and has been verified in Ubuntu 14.04 using GNU bash 4.3.11(1), so it should work on any recent GNU bash version.

1
  • @doubledecker -- this looks like it satisfies your conditions, so can be accepted as the answer. Also, +1 as I've tested this under GNU bash (version 4.3.11(1)-release (i686-pc-linux-gnu)) in Ubuntu 14.04.
    – belacqua
    Jun 20, 2014 at 19:47
15

It's an old post but I just found it now...

Instead of redirecting the output to > /dev/null you can redirect it to the last file:

echo "foobarbaz" | tee file1 > file2

Or for appending the output:

echo "foobarbaz" | tee -a file1 >> file2
1
  • this is more or less what other answer said (except for -a in tee)
    – Archemar
    Apr 29, 2016 at 9:19
4

As @jofel mentioned in a comment under the answer, this can be done natively in zsh:

echo foobar >file1 >file2 >file3

or, with brace expansion:

echo foobar >file{1..3}

Internally this works very similarly to the tee answers provided above. The shell connects the command's stdout to a process that pipes to multiple files; therefore, there isn't any compelling technical advantage to doing it this way (but it does look real good). See the zsh manual for more.

0
3

@strugee's answer for zsh is not safe to use. The safe way to do this would be to enclose some_command in brace brackets like:

{some_command} >output_log_1 >output_log_2

and not like this:

some_command >output_log_1 >output_log_2

zsh manual's Multios section explains the reason with an example.

There is a problem when an output multio is attached to an external program. A simple example shows this:

cat file >file1 >file2
cat file1 file2

Here, it is possible that the second ‘cat’ will not display the full contents of file1 and file2 (i.e. the original contents of file repeated twice).

The reason for this is that the multios are spawned after the cat process is forked from the parent shell, so the parent shell does not wait for the multios to finish writing data. This means the command as shown can exit before file1 and file2 are completely written. As a workaround, it is possible to run the cat process as part of a job in the current shell:

{ cat file } >file1 >file2

Here, the {...} job will pause to wait for both files to be written.

So the safe way would be to always enclose the command in braces if one wants to be sure that the copy finishes before they can consume it in following commands.

2

Unable to comment, however, another way to express

echo "foobarbaz" | tee file1 file2 file3 file4 file5 file6 file7 file8 > /dev/null

Could be simplified to this, when dealing with many files.

echo "foobarbaz" | tee file{1..8} > /dev/null
2
  • 2
    How is this really different from the other answers already given? Especially since few people likely want literal file1 through file8 as their names and those are likely just example placeholders for the names of the files Dec 28, 2015 at 14:56
  • 1
    Likely or not, this is exactly the solution I needed, and thought it could help someone else.
    – user149146
    Dec 28, 2015 at 19:32

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