tee(2)
๐ tee(2) ๐
okay bestie, so this is the system call manual page for tee โ that's <hotpink>tee(2)</hotpink> from the System Calls Manual section ๐
(yes, there's also a tee(1) command and no, they are not the same thing, don't @ me). this whole page lives in the Linux man-pages, and we're gonna get through it together, no cap โจ
๐ธ NAME ๐ธ
tee โ duplicating pipe content ๐ซถ
๐ LIBRARY ๐
Standard C library (libc, -lc) โ so pretty standard, iconic behavior, lowkey nothing fancy ๐
โจ 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);
not me needing that #define _GNU_SOURCE first or the compiler just ghosting me ๐ญ it's the See feature_test_macros(7) thing, keep it bestie.
๐ฆ 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. ๐ช
and here's the gag, the main character twist: it does not consume the data that is duplicated from fd_in. so that data stays put and can be copied by a subsequent splice(2). it's giving "borrow your notes but you still have your notes" energy ๐๐
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 fortee(); seesplice(2). it's just there, vibing ๐SPLICE_F_NONBLOCKโ do not block on I/O; seesplice(2)for further details. the โจimpatientโจ option, we love herSPLICE_F_MOREโ currently has no effect fortee(), but may be implemented in the future; seesplice(2). she's a maybe, a future era ๐ญSPLICE_F_GIFTโ unused fortee(); seevmsplice(2). literally unused, she came to the party with nothing ๐๐
๐ 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. so 0 = nobody's writing, nothing to do, go home girl ๐
on error, tee() returns -1 and errno is set to indicate the error. classic ๐
๐ ERRORS ๐
EAGAINโSPLICE_F_NONBLOCKwas specified in flags or one of the file descriptors had been marked as nonblocking (O_NONBLOCK), and the operation would block. she said "i would block but i'm literally not allowed to" ๐ โ๏ธEINVALโfd_inorfd_outdoes not refer to a pipe; orfd_inandfd_outrefer to the same pipe. bestie these are PIPES only, and they gotta be different pipes, iykyk ๐ซENOMEMโ out of memory. the laptop said no ๐ช๏ธ
๐ STANDARDS ๐
Linux. that's it, that's the standard ๐
โญ HISTORY โญ
Linux 2.6.17, glibc 2.5. a lil retro moment, we respect the elders ๐ฐ๏ธ
๐ NOTES ๐
conceptually, tee() copies the data between the two pipes. but 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. ๐ซข
the way that it's fake-copying but actually just holding a reference?? efficiency icon fr fr, not a single byte was moved and yet โจeverythingโจ happened. no cap.
๐ง EXAMPLES ๐ง
the example below implements a basic tee(1) 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
see how date's output went into the file and to cat? that's the whole point bestie, oh she ATE ๐
๐ 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);
}
copy it exactly, bestie, every include, every SSIZE_MAX-adjacent vibe, don't be "fixing" the indentation ๐
๐ฅ SEE ALSO ๐ฅ
splice(2), vmsplice(2), pipe(7) โ the whole friend group, go read them too ๐ซถ
and that's the page!! ๐ this was Linux man-pages 6.20, dated 2026-02-08 โ shoutout to the man-pages girlies for keeping the tee(2) lore alive, ilysm, drink water, and remember: tee() never consumes, she only shares ๐โจ๐๐ฆ๐ธ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 2006, Jens Axboe <axboe@kernel.dk>
- Copyright 2006, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms