Hubbry Logo
Tee (command)Tee (command)Main
Open search
Tee (command)
Community hub
Tee (command)
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Tee (command)
Tee (command)
from Wikipedia
tee
DevelopersAT&T Bell Laboratories, Mike Parker, Richard Stallman, David MacKenzie, Microware, Jim Hall, JP Software, Microsoft
Initial releaseJune 1974; 51 years ago (1974-06)
Written inC
Operating systemUnix, Unix-like, Plan 9, Inferno, OS-9, FreeDOS, Windows, ReactOS, IBM i
PlatformCross-platform
TypeCommand
LicenseFreeDOS: GPL-2.0-or-later
ReactOS: GPLv2
Plan 9: MIT License

tee is shell command that copies data from standard input to one or more files in addition to standard output; duplicating the input to each output.[1] The name derives from the tee pipe fitting even though the tee command duplicates the input into each output instead of dividing the input into portions for each output.[2] The command is often used with pipes and filters. Similar behaving commands are provided by many shells although syntax varies.

The command is provided in Unix and Unix-like systems, OS-9,[3] DOS (e.g. 4DOS, FreeDOS), Windows (e.g. 4NT, PowerShell, UnxUtils[4]), ReactOS[5] and IBM i.[6]

The Linux version was written by Mike Parker, Richard Stallman, and David MacKenzie.[7]

The FreeDOS version was developed by Jim Hall and is licensed under the GPL.[8]

Additionally the sponge[9] command offers similar capabilities.

Unix

[edit]

The typical syntax on a Unix-based system can be described as:

tee [-a] [-i] [file...]
  • file... One or more names for files to receive the command input data
  • -a Append to a file rather than overwriting
  • -i Ignore interrupts

Process substitution lets more than one process read the standard output of the originating process.[10].

If a write to any file is not successful, writes to other files and standard output continue, but the exit status will indicate failure with a value greater than 0.

Examples

[edit]

The following both displays the output of the lint program.c command and saves the output to a file named program.lint; overwriting it if it already existed.

lint program.c | tee program.lint

The following does the same as the previous example, except for appending the output to an existing file instead of overwriting it. The file is created if it was not pre-existing.

lint program.c | tee -a program.lint

The following bypasses a limitation of the sudo command which is unable to pipe standard output to a file. By dumping its stdout stream into /dev/null, output to the console suppressed. This gives the current user root access to a server over ssh, by installing the user's public key to the server's key authorization list.

cat ~/.ssh/id_rsa.pub | ssh admin@server "sudo tee -a /root/.ssh/authorized_keys2 > /dev/null"

In Bash, the output can be filtered before being written to the file—without affecting the output displayed—by using process substitution. The following removes common ANSI escape codes before writing to ls.txt, but retains them for display.[11]

ls --color=always | tee >(sed "s/\x1b[^m]*m//g" > ls.txt)

4DOS and 4NT

[edit]

The syntax on 4DOS and 4NT can be described as:

TEE [/A] file...
  • file One or more names for files to receive the command input data
  • /A Append to a file rather than overwriting

When used with a pipe, the output of the previous command is written to a temporary file. When that command finishes, tee processes the temporary file; copying it to the file argument(s) and standard output.

Examples

[edit]

This example searches the file wikipedia.txt for any lines containing the string "4DOS", makes a copy of the matching lines in 4DOS.txt, sorts the lines, and writes them to the output file 4DOSsorted.txt:

>find "4DOS" wikipedia.txt | tee 4DOS.txt | sort > 4DOSsorted.txt

PowerShell

[edit]

Powershell is not suitable for binary and raw data, always treats the stream as text, and modifies the data as it is transferred.

The syntax on PowerShell can be described as:

tee [-FilePath] <String> [-InputObject <PSObject>]
tee -Variable <String> [-InputObject <PSObject>]
  • -InputObject <PSObject> Specifies the object input to the cmdlet. The parameter accepts variables that contain the objects and commands or expression that return the objects.
  • -FilePath <String> Specifies the file where the cmdlet stores the object. The parameter accepts wildcard characters that resolve to a single file.
  • -Variable <String> A reference to the input objects will be assigned to the specified variable.

The command is implemented as a ReadOnly command alias that invokes the cmdlet named Microsoft.PowerShell.Utility\Tee-Object.

Examples

[edit]

The following displays the standard output of command ipconfig in the console window, and simultaneously saves a copy of it in the file OutputFile.txt.

ipconfig | tee OutputFile.txt

The following shows that the piped input for tee can be filtered and that tee is used to display that output, which is filtered again so that only processes owning more than 1000 handles are displayed, and writes the unfiltered output to the file ABC.txt. To display and save all running processes, filtered so that only programs starting with svc and owning more than 1000 handles are output:

Get-Process | Where-Object { $_.Name -like "svc*" } | Tee-Object ABC.txt | Where-Object { $_.Handles -gt 1000 }

See also

[edit]

References

[edit]

Further reading

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
In operating systems, the tee command is a standard utility that reads data from standard input and writes it simultaneously to both standard output and one or more specified files, effectively duplicating the input stream. The command's name derives from the T-splitter fitting used in , which divides a single flow into two directions. Originating from developed at Research in June 1974, tee has become a core component of Unix pipelines for tasks like and monitoring output without interrupting data flow. As defined in the standard (IEEE Std 1003.1-2017), tee copies standard input to standard output and zero or more files without buffering output, overwriting files by default unless the -a (append) option is specified. The basic synopsis is tee [-ai] [file...], where -i ignores interrupt signals like SIGINT to prevent premature termination. In Coreutils implementations, which are widely used in distributions, additional options such as -p or --output-error allow control over error handling for write failures, and specifying - as a filename duplicates output to standard output again. The tee command is particularly valuable in shell scripting and command-line workflows for splitting output streams, such as capturing verbose program logs to a file while displaying them on —for instance, ps aux | tee processes.log lists running processes and saves the result to a file. It supports in shells like Bash for parallel operations, such as computing checksums or compressing data in multiple directions. is 0 on success and nonzero on failure, ensuring reliable integration in automated scripts.

Overview

Purpose and Functionality

The tee utility reads from standard input (stdin) and writes the data simultaneously to standard output (stdout) as well as to one or more specified files, effectively duplicating the input stream without altering its original flow. This dual-output mechanism allows users to capture and persist data to files while continuing to process it through pipelines or further commands. A core function of tee is the non-destructive duplication of streams, which facilitates real-time monitoring, logging, or archiving in command-line workflows without interrupting the primary path. By preserving the of stdout, tee ensures that downstream processes receive the complete input as if no duplication occurred, while independently handling file writes to enable persistent storage. In operation, tee processes input from stdin in a linear fashion: it reads blocks of , echoes them verbatim to stdout to maintain continuity, and concurrently appends or overwrites the same to target files, thereby adding persistence without affecting error streams like stderr, which remain separate. This behavior supports seamless integration in environments, where tee is standardized under for portable use across compliant systems. The command derives its name from the T-junction in plumbing, symbolizing its role in branching a single input flow to multiple outputs, a concept originating from early Unix tools designed for efficient stream manipulation.

Historical Development

The tee command originated as part of the early Unix operating system developed at in the by and , forming a key component of the initial Unix toolkit designed for efficient data processing and pipeline integration. It first appeared in the Fifth Edition of Unix in 1974, and was included in , released in 1979, among the standard utilities for duplicating input streams. The command's design reflected the minimalist philosophy of early Unix, enabling simple yet powerful I/O manipulations without complex buffering. Standardization efforts began in the late 1980s to promote portability across systems, with POSIX.1-1988 formally defining the core behavior of tee, including its requirement to copy standard input to standard output and files without buffering. This specification ensured consistent implementation in diverse environments, establishing tee as a foundational utility for compliant systems. Subsequently, the command was incorporated into the (SUS) across its versions, from SUSv1 onward, reinforcing its role in ensuring interoperability in modern BSD and distributions. In parallel, the GNU Project extended tee's functionality starting in the 1980s through its core utilities suite, with significant evolution in the merged GNU coreutils package formalized in 2002 from earlier components like shellutils. GNU implementations include the POSIX-standard options as well as extensions beyond the baseline, such as --output-error for controlling error handling on write failures. These developments maintained backward compatibility while enhancing utility for logging and incremental data handling in GNU environments.

Syntax and Options

Core Syntax

The core syntax of the tee command follows the form tee [options] [file ...], where it reads data from standard input (stdin) and simultaneously writes that data to standard output (stdout) as well as to each specified file. This structure adheres to the standard, enabling consistent behavior across compliant systems. The positional arguments [file ...] represent one or more target files to which the input is copied; if no files are provided, tee simply passes the input through to stdout without additional writes. By default, tee overwrites the content of any existing target files with the new data, truncating them if necessary to start writing from the beginning. In Unix-like environments, tee integrates seamlessly into pipelines, such as command | tee file, where the output from the preceding command flows into tee via stdin, allowing duplication to a file while preserving the stream to subsequent commands or stdout. When multiple files are specified (e.g., tee file1 file2), tee writes the identical input content to each file in the order listed, ensuring synchronized duplication unless modified by options.

Standard Options

The tee utility supports a limited set of standard options as mandated by the specification, which ensure portability across compliant systems. These options modify the behavior of file output handling and without introducing implementation-specific extensions. The -a or --append option directs tee to the input data to the specified files rather than overwriting their existing contents, thereby preserving prior data in those files. This is particularly useful for accumulating output over multiple invocations without data loss. When specified, -a applies uniformly to all file operands provided on the command line. The -i or --ignore-interrupts option causes tee to disregard the SIGINT signal, typically generated by an interrupt such as Ctrl+C from the terminal, allowing the command to continue processing input until completion or another termination condition. This ensures uninterrupted operation in interactive environments where accidental signals might otherwise halt the prematurely. Like -a, the -i option takes effect globally for the entire invocation. In accordance with utility syntax guidelines, options such as -a and -i must precede any file arguments on the command line, facilitating predictable parsing and avoiding ambiguity between options and operands. This requirement guarantees that all options influence the handling of every specified output file consistently, promoting reliable behavior in scripts and pipelines across POSIX-compliant implementations.

Implementation-Specific Options

The GNU implementation of the tee command, as provided in coreutils for systems, extends the standard with several additional options for enhanced usability and diagnostics. These include --help, which displays a brief usage summary and exits, and --version, which outputs version information including the build date and copyright details. Furthermore, the -p or --output-error[=MODE] option allows configuration of error handling when writing to pipes or files fails, with modes such as warn (diagnose errors writing to any output, continue writing to remaining outputs, and set to indicate failure if any output errors occurred), warn-nopipe (default: diagnose errors writing to any output not a pipe, but on a pipe error exit immediately), exit (exit immediately on error writing to any output), and exit-nopipe (exit immediately on error writing to any output not a pipe, but on a pipe error exit immediately). This option directs diagnostic messages to , preserving the integrity of the primary data stream to standard output and files. In contrast, BSD variants such as FreeBSD adhere closely to the POSIX specification, supporting only the standard -a (append) and -i (ignore interrupts) options without GNU's extensions like --help, --version, or -p. The BusyBox implementation, designed for embedded systems and resource-constrained environments, further trims options to maintain a small footprint, supporting solely -a for appending output and -i for ignoring interrupt signals, omitting non-essential features like help or version displays. The -p option in tee particularly aids debugging in complex by isolating error diagnostics to , allowing developers to monitor issues such as broken pipes without corrupting the forwarded data stream—for instance, in scenarios where a subsequent command in the pipeline fails unexpectedly.

Usage Examples

Basic Redirection

The tee command facilitates basic input/output redirection by reading from standard input and simultaneously writing the data to both standard output (for immediate display) and one or more specified files. This duplication allows users to capture output without interrupting the normal flow of command results to . A simple example demonstrates this functionality: executing echo "Hello" | tee output.txt sends the string "Hello" to both the screen (via stdout) and the file output.txt, creating or overwriting the file with the content. Similarly, cat file.txt | tee backup.txt reads the contents of file.txt and duplicates them to both the terminal display and backup.txt, effectively creating a copy while showing the original data. By default, tee overwrites any existing target files, which can be verified post-execution using commands like ls to confirm the file's presence and size or cat backup.txt to inspect its contents. This overwrite behavior makes tee particularly useful for quick logging, such as saving the output of a command to a file for later review while continuing to observe it in real-time on the display. For scenarios requiring addition to existing files rather than replacement, the -a (append) option can be used, as detailed in the standard options section.

Pipeline Integration

The tee command serves as a key component in Unix pipelines by acting as a non-terminating splitter that duplicates standard input to both standard output—for continued processing downstream—and one or more files, thereby enabling efficient branching in sequential command flows without interrupting the data stream. This functionality allows upstream commands to generate output that is simultaneously captured for persistent storage and passed to subsequent commands for further manipulation, such as filtering or sorting. In essence, tee facilitates the creation of "T-shaped" data paths in pipelines, mirroring the plumbing fitting from which it derives its name, and supports real-time processing by writing data without full buffering to ensure low-latency propagation through the chain. A practical illustration of this integration is the pipeline ls -l | tee listing.txt | grep ".txt", where ls -l generates a detailed directory listing, tee saves the complete output to listing.txt while forwarding it unchanged, and grep then filters the stream to display only lines containing ".txt" for text files. Similarly, ps aux | tee processes.log | sort -k1 captures the full process list from ps aux into processes.log for logging purposes, then sorts the output by the first field (typically the user) to organize the data for analysis. These examples highlight tee's role in maintaining pipeline continuity, as the command does not consume or alter the data stream, allowing downstream utilities to operate on the original input. Particularly in long-running processes, [tee](/page/Tee) enhances efficiency by enabling simultaneous monitoring and archival without performance bottlenecks, as seen in tail -f /var/log/syslog | tee monitor.log, which continuously tails the system log in real-time, duplicates the output to monitor.log for offline review, and permits further piping if needed—all while adhering to unbuffered I/O to minimize delays in interactive sessions. This approach is especially valuable in system administration tasks, where preserving the full data flow for both immediate display and historical records prevents the need for separate invocations or complex scripting.

Error Handling Scenarios

The tee command handles errors encountered during output operations in a manner that prioritizes continuity for standard output while signaling failures appropriately. For instance, when attempting to write to a protected file without sufficient permissions, such as in the command echo "data" | tee /root/protected.txt, tee will output an error message to indicating "permission denied" for the file, but it will still successfully duplicate the input "data" to standard output, allowing the to proceed. This default behavior ensures that non-pipe outputs (like files) do not halt the overall process unless explicitly configured otherwise via the --output-error option. In scenarios involving write failures, such as running out of disk space while appending to a file, tee will diagnose the (e.g., "No space left on device") and exit with a non-zero status code to indicate failure, but it preserves any partial output already written to the file and continues directing input to standard output and other valid destinations. This partial preservation is crucial for applications where complete data loss is undesirable, though the non-zero can be trapped in scripts for handling, such as command | tee log.txt || echo "Error occurred". Interruptions, like those from Ctrl+C (SIGINT), can also affect tee's operation, but the -i or --ignore-interrupts option allows it to persist through such signals. For example, executing yes | tee -i output.txt and then pressing Ctrl+C will cause tee to ignore the interrupt, continuing to write the infinite "y" output to the file without termination. Without this option, the interrupt would stop tee immediately, potentially leaving incomplete output.

Platform Implementations

Unix-like Systems

The tee command is installed by default at /usr/bin/tee on most systems, forming a core component of . In environments, it is provided by the GNU coreutils package, ensuring consistent availability across distributions such as and . In BSD systems like , tee is part of the base system installation, accessible without additional packages and aligned with standards for portability. Implementations in Unix, , and BSD strictly follow the specification, which mandates that tee copies standard input to both standard output and one or more files while avoiding output buffering to maintain real-time processing. This adherence ensures predictable behavior, including overwriting files by default or appending with the -a option, and continuing operations despite errors on individual outputs. Atomic writes are supported where feasible through underlying calls, particularly for data sizes up to the PIPE_BUF limit when piping to or from tee. The definition of tee traces back to early Unix standards, providing a foundation for its role in command-line workflows. In with tee implementation, newly created files receive permissions masked by the current process , typically resulting in modes like 0644 for regular files unless overridden, to prevent unintended access. FreeBSD's tee operates within the system's enhanced security model, where file flags—such as or immutable attributes set via chflags—offer additional protections that restrict writes or deletions even if standard permissions permit them. For integration with shells like bash, tee handles encoded input and output natively when the locale is set to (e.g., via LANG=en_US.[UTF-8](/page/UTF-8)), preserving character in pipelines without requiring special flags.

Windows Environments

The Windows Command Prompt (CMD) does not include a native tee command among its built-in utilities, requiring users to seek emulations or third-party implementations for duplicating standard input to both output and files. Common emulations in CMD involve batch scripts that approximate tee's behavior, such as Tee.bat, which redirects piped input to the console while appending it to a specified file without blocking the pipeline. For instance, a command like dir | teebat log.txt displays the directory listing on screen and saves it to log.txt simultaneously. These scripts leverage native redirection operators like > and type but may introduce minor delays or require synchronous execution in simpler variants. Third-party ports provide more faithful reproductions of tee; the GNU Coreutils package via GnuWin32 has offered a Windows-native tee.exe since version 5.3.0, released in April 2005, allowing POSIX-like syntax in CMD or other shells. Additionally, starting with Windows 10 version 2004 (build 19041) and , the (WSL) integrates full Unix command sets, including the standard tee from distributions like , enabling seamless use within a Linux environment on Windows without additional installations. These ported implementations generally adhere to standards for handling but adapt to Windows file systems by supporting drive letters and paths; for example, ipconfig | tee C:\network_log.txt captures network configuration output to a file on the C: drive while displaying it in the console. However, cross-platform scenarios introduce challenges with line endings, as Windows defaults to return-line feed (CRLF) sequences (\r\n) for newlines, while Unix employs line feed (LF) alone (\n). Tee commands preserve the input stream's byte-for-byte format, so Unix-sourced input with LF endings written via a Windows port may produce files that render incorrectly in native Windows text editors or scripts expecting CRLF, necessitating tools like dos2unix for conversion.

DOS and Legacy Shells

In DOS-based environments, the tee functionality was implemented as a built-in command in enhanced command-line interpreters like , developed by JP Software and first released in 1989 as a replacement for the standard shell in systems. This command duplicated standard input to standard output while simultaneously writing it to specified files, enabling piping operations in resource-constrained legacy setups where tools were unavailable. 4DOS's TEE supported basic redirection similar to its Unix counterpart, allowing users to capture pipeline output for logging or further processing within batch files and scripts, with options like /A for appending to files. The successor to 4DOS, 4NT, introduced in the early 1990s for environments, retained and expanded the TEE command with deeper integration into processing. This allowed for more flexible duplication of stdout in automated tasks, building on 4DOS's foundation while adapting to NT's command processor () limitations. Both implementations emphasized compatibility with piping, making TEE a key tool for developers working in replacement environments. Behaviorally, in these shells supported an mode (via options like /A) to add output to existing files without overwriting, but operations were constrained by DOS's inherent limit of 255 maximum open file handles per , which could lead to errors in complex pipelines involving multiple files. This restriction stemmed from the underlying architecture, where file handle allocation was managed globally and could not exceed 255 without custom extensions or managers. As a result, users in legacy DOS setups often optimized usage to avoid exhausting handles during batch executions or multi-file . FreeDOS, an open-source DOS-compatible operating system first released in 1998 and maintained as of 2025, includes a standard tee command in its base utilities package. This implementation reads from standard input, writes to standard output, and copies to one or more files, with syntax tee [options] [file], supporting append mode via options and adhering to basic Unix-like behavior for legacy compatibility.

PowerShell Variant

In PowerShell, the equivalent functionality to the Unix tee command is provided by the Tee-Object cmdlet, which was introduced in PowerShell 1.0 in November 2006 as part of the Microsoft.PowerShell.Utility module. This cmdlet redirects pipeline input to both a specified destination—such as a file or variable—and the console output, while preserving the original data flow for further processing. Unlike traditional text-based tee implementations, Tee-Object operates on .NET objects, enabling it to handle structured data like arrays, custom objects, or process information without immediate serialization to plain text, thus maintaining object properties throughout the pipeline. The basic syntax for Tee-Object includes parameters such as -FilePath for writing to a file, -Variable for storing in a variable, -Append (introduced in PowerShell 3.0 for appending to existing files rather than overwriting), and -InputObject to specify objects directly rather than relying solely on pipeline input. For instance, the command Get-Process | Tee-Object -FilePath log.txt | Sort-Object retrieves running processes, saves them to log.txt while displaying and sorting them in the console, demonstrating seamless integration with PowerShell's object-oriented . This object-aware behavior allows for advanced scenarios, such as teeing an array of custom objects for parallel and filtering: $data = [PSCustomObject]@{Name='Item1'; Value=10}, [PSCustomObject]@{Name='Item2'; Value=20}; $data | Tee-Object -Variable backup | Where-Object {$_.Value -gt 15}, where the full object is retained in both the backup variable and the filtered output. PowerShell's Tee-Object enhances scripting flexibility by leveraging the language's , where input objects are serialized only when written to files (using encoding by default in PowerShell 6 and later), but remain fully functional .NET instances when assigned to variables or passed downstream. This contrasts with stream-based tools by supporting pipeline-aware operations on complex data types, making it particularly useful in tasks involving structured output from cmdlets like Get-Process or Get-Service.

Common Pitfalls and Best Practices

Security Considerations

One significant risk associated with the tee command is its default behavior of overwriting existing files when writing output, which can lead to unintended for critical system or log files if not carefully managed. To mitigate this, users should verify file and permissions prior to invocation, such as by employing a conditional check like test -f file in scripts before to tee. The append option (-a) provides a straightforward by adding content to the end of files rather than replacing them, preserving prior data. In multi-user Unix-like environments, tee operates under the invoking user's permissions, inheriting the process's effective user ID and group ID, which can expose sensitive files to modification if output is piped from untrusted or malicious sources. For instance, in scenarios involving unverified input pipelines, an attacker could inject harmful content into privileged files, emphasizing the need to avoid tee in setuid (SUID) contexts or with untrusted streams to prevent unauthorized exposure or escalation. When elevating privileges, should be used with judiciously, such as in command | sudo tee /path/to/root/file, to write to protected locations, but this amplifies risks if input is not sanitized; instead, prefer direct redirection operators like > for root-owned files to minimize exposure.

Performance Implications

The tee command is specified to operate without buffering output, meaning it performs writes immediately rather than accumulating data in buffers, which supports timely in pipelines but allows implementations to optimize beyond single-byte operations for . This unbuffered behavior ensures that data flows through without artificial delays from tee itself, though real-time in Unix pipelines can still be affected by buffering in preceding commands, leading to potential stalls until buffers fill. To address this, the stdbuf utility can enforce line buffering (e.g., stdbuf -oL command | tee file), flushing output after each newline for more responsive handling in interactive or monitoring scenarios. In high-volume data scenarios, GNU tee's resource usage remains low in terms of CPU overhead, as it primarily involves duplicating input to multiple destinations without complex . However, when writing to multiple files, the I/O load scales linearly with the number of outputs, since each file requires a separate write operation, effectively multiplying disk throughput demands—for instance, directing output to two files doubles the total written to storage compared to a single output. This disk I/O intensification is the primary bottleneck, particularly in pipelines large datasets, where it can extend execution times proportional to the output count and storage speed. For scalability implications, tee performs adequately on modern hardware but can introduce overhead in resource-constrained environments, such as embedded systems with limited memory, where the primary costs are I/O-related rather than memory usage. In such cases, the variant optimizes for minimal footprint and low-resource operation, making it suitable for embedded deployments despite potential trade-offs in raw throughput for I/O-heavy tasks. As detailed in system implementations, this unbuffered design underpins tee's efficiency in standard pipelines.

Alternatives Comparison

The tee command differs from standard output redirection operators such as > file and >> file, which direct a command's output solely to a file while suppressing display on standard output (stdout), thereby preventing real-time monitoring or further ; in contrast, tee duplicates the input stream to both stdout and one or more files, enabling simultaneous and continuation in pipelines. Unlike command, which records an entire interactive terminal session—including user input, control characters, and timing information for potential replay—tee focuses exclusively on duplicating stdout (or combined stderr via redirection) without capturing terminal-specific artifacts like tty interactions. In scripting scenarios, a one-off use of tee processes a single command's output, whereas employing exec > >(tee log) at the script's outset redirects the entire shell's stdout persistently to both the terminal and a log file via , allowing comprehensive session logging without repeated invocations. Tools like multitail build upon tee-like functionality by supporting , filtered output to multiple files or commands, with options to write pre- or post-filtered streams, thus extending tee for advanced multi-file monitoring and visualization.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.