tee

package
v0.0.30 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package tee implements the tee builtin command.

tee — read from standard input and write to standard output and files

Usage: tee [OPTION]... [FILE]...

Copy standard input to standard output, making a copy in each FILE operand. A FILE operand of "-" is opened as a literal file named "-" through the sandbox like any other operand — GNU tee treated "-" as an alias for stdout in coreutils 5.3.0 through 8.23, but that behavior was removed in 8.24 as mandated by POSIX, and this implementation matches current GNU tee rather than the historical behavior.

tee mutates file content, so — like truncate, logrotate, rm, and systemctl — it is only available in remediation mode. This is enforced both at interpreter dispatch (RemediationOnly) and again inside the handler as defence in depth, per docs/RULES.md.

Accepted flags:

-a, --append
    Append to the given FILEs, rather than overwriting them.

-h, --help
    Print usage to stdout and exit 0.

Out of scope (not implemented; rejected as unknown flags):

-i, --ignore-interrupts   ignore SIGINT — rshell builtins do not
                          receive process signals the way a forked
                          tee(1) would, so this flag has no
                          meaningful effect to implement.
-p, --output-error[=MODE] behavior on write error / FIFO-aware
                          diagnostics — every FIFO write target is
                          already rejected before open (see "File
                          access" below), so the "pipe error" modes
                          GNU tee -p exists for cannot occur here.

File access:

Every FILE operand is opened via callCtx.OpenFile with O_WRONLY |
O_CREATE, plus O_APPEND (-a) or O_TRUNC (default). This routes through
the same AllowedPaths sandbox and remediation-mode gate that backs the
shell's own >/>> redirects and the truncate builtin: writes are
confined to :rw roots and a multiply linked (hard-linked) regular file
is rejected as a write target. A non-regular target (FIFO, socket,
device) is rejected by a non-blocking pre-open StatFile check
(rejectNonRegularTarget) so the shell does not block waiting for a
FIFO reader; see that function's doc comment for the narrow TOCTOU
window this leaves and why it is an accepted, low-severity gap shared
with the interpreter's own >/>> redirects. See the hard link entry in
AGENTS.md/docs/RULES.md for the shared hard-link mechanism.

Exit codes:

0  Standard input was fully copied to standard output and to every
   FILE operand without error.
1  Standard input could not be fully read, or at least one FILE
   operand could not be opened or written. tee continues copying to
   the remaining destinations after a single non-pipe destination
   fails (open/write/close error), matching GNU tee's default
   "diagnose errors writing to non pipe outputs" behavior.

   A broken pipe (EPIPE) on any destination — in practice this is
   standard output when a downstream reader like `head` exits early
   — is handled differently: matching GNU tee's other documented
   default, "exit immediately on error writing to a pipe", the whole
   copy stops there rather than continuing to write to the remaining
   destinations. This case exits 0, matching cat's handling of the
   same condition: a downstream reader exiting early is normal,
   expected pipeline behavior, not a reportable error.

Memory safety:

Input is streamed from stdin in fixed-size chunks (teeBufSize, 32
KiB) and fanned out to standard output and every open FILE in the same
iteration — no buffering proportional to total input size. The read
loop checks ctx.Err() before every Read to honour the shell's
execution timeout and support graceful cancellation, and also returns
as soon as every destination (including standard output) has stopped
accepting writes, rather than draining a long-lived or infinite stdin
source to no purpose. The number of concurrently open file descriptors
is bounded by MaxFileOperands, checked before any FILE is opened, so a
glob or repeated operand cannot exhaust the embedding process's
descriptor table; a later duplicate name reuses its own file handle
rather than reopening (matching GNU tee's write-order and open-once
semantics).

Index

Constants

View Source
const MaxFileOperands = 64

MaxFileOperands is the maximum number of FILE operands accepted by a single tee invocation. Every accepted operand is opened and held open concurrently for the whole duration of the copy (see the package doc comment) — unlike jq/sha256sum, which open, process, and close one file at a time, tee cannot release a descriptor until every destination has been written to and closed. An unbounded operand count — easily reached through shell glob expansion — would therefore open and hold an unbounded number of file descriptors, exhausting the embedding process's descriptor table (starving unrelated concurrent work in that process) well before any generous-looking cap like 1,024 is reached: macOS's default soft RLIMIT_NOFILE is 256, and a cap needs headroom below the lowest common default for stdin/stdout/stderr and any sandbox-internal descriptors already open in the embedding process. 64 matches jq's existing MaxOperands precedent for simultaneously-relevant file operands, with a wide margin below 256. Exceeding the limit rejects the entire command before any destination is opened.

Variables

View Source
var Cmd = builtins.Command{
	Name:            "tee",
	Description:     "read from standard input and write to standard output and files",
	MakeFlags:       registerFlags,
	RemediationOnly: true,

	RemediationDeniedMessage: readOnlyMessage,
}

Cmd is the tee builtin command descriptor.

Functions

This section is empty.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL