---
name: "tee"
section: "2"
platform: "linux"
package: "man-pages"
version: "6.19-1"
release: "Arch Linux 2026-11-01"
platforms: ["linux"]
url: "https://new.carta.tech/tee/2"
---

# tee(2)

duplicating pipe content

## NAME

tee - duplicating pipe content

## LIBRARY

Standard C library (*libc*, *-lc*)

## SYNOPSIS

```
#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <fcntl.h>
```

**ssize\_t tee(int** *fd\_in***, int** *fd\_out***, size\_t** *size***, unsigned int** *flags***);**

## DESCRIPTION

**tee**() duplicates up to *size* bytes of data from the pipe referred to by the file descriptor *fd\_in* to the pipe referred to by the file descriptor *fd\_out*. It does not consume the data that is duplicated from *fd\_in*; therefore, that data can be copied by a subsequent [**splice**(2)](/splice/2.md).

*flags* is a bit mask that is composed by ORing together zero or more of the following values:

- **SPLICE\_F\_MOVE**

  Currently has no effect for **tee**(); see [**splice**(2)](/splice/2.md).

- **SPLICE\_F\_NONBLOCK**

  Do not block on I/O; see [**splice**(2)](/splice/2.md) for further details.

- **SPLICE\_F\_MORE**

  Currently has no effect for **tee**(), but may be implemented in the future; see [**splice**(2)](/splice/2.md).

- **SPLICE\_F\_GIFT**

  Unused for **tee**(); see [**vmsplice**(2)](/vmsplice/2.md).

## RETURN VALUE

Upon successful completion, **tee**() returns the number of bytes that were duplicated between the input and output. A return value of 0 means that there was no data to transfer, and it would not make sense to block, because there are no writers connected to the write end of the pipe referred to by *fd\_in*.

On error, **tee**() returns -1 and *errno* is set to indicate the error.

## ERRORS

- **EAGAIN**

  **SPLICE\_F\_NONBLOCK** was specified in *flags* or one of the file descriptors had been marked as nonblocking (**O\_NONBLOCK**)**,** and the operation would block.

- **EINVAL**

  *fd\_in* or *fd\_out* does not refer to a pipe; or *fd\_in* and *fd\_out* refer to the same pipe.

- **ENOMEM**

  Out of memory.

## STANDARDS

Linux.

## HISTORY

Linux 2.6.17, glibc 2.5.

## NOTES

Conceptually, **tee**() copies the data between the two pipes. In reality no real data copying takes place though: under the covers, **tee**() assigns data to the output by merely grabbing a reference to the input.

## EXAMPLES

The example below implements a basic [**tee**(1)](/tee/1.md) program using the **tee**() system call. Here is an example of its use:

```
$ date | ./a.out out.log | cat;
Tue Oct 28 10:06:00 CET 2014
$ cat out.log;
Tue Oct 28 10:06:00 CET 2014
```

### Program source

```
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
    int      fd;
    ssize_t  size, ssize;
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }
    for (;;) {
        /*
         * tee stdin to stdout.
         */
        size = tee(STDIN_FILENO, STDOUT_FILENO,
                  INT_MAX, SPLICE_F_NONBLOCK);
        if (size < 0) {
            if (errno == EAGAIN)
                continue;
            perror("tee");
            exit(EXIT_FAILURE);
        }
        if (size == 0)
            break;
        /*
         * Consume stdin by splicing it to a file.
         */
        while (size > 0) {
            ssize = splice(STDIN_FILENO, NULL, fd, NULL,
                          size, SPLICE_F_MOVE);
            if (ssize < 0) {
                perror("splice");
                exit(EXIT_FAILURE);
            }
            size -= ssize;
        }
    }
    close(fd);
    exit(EXIT_SUCCESS);
}
```

## SEE ALSO

[**splice**(2)](/splice/2.md), [**vmsplice**(2)](/vmsplice/2.md), [**pipe**(7)](/pipe/7.md)
