Detecting Output Destination in CLI Programs
Programs can detect where their output is going (terminal vs pipe vs file) and modify their behavior accordingly. This is why tools like rg
, ls
, and git
show colors when output goes to your terminal but plain text when piped to other commands.
How It Works in C
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
int main() {
if (isatty(STDOUT_FILENO)) {
printf("\033[32mOutput going to terminal - colors enabled!\033[0m\n");
} else {
printf("Output piped/redirected - plain text\n");
}
// More detailed detection
struct stat st;
fstat(STDOUT_FILENO, &st);
if (S_ISREG(st.st_mode)) {
printf("Redirected to regular file\n");
} else if (S_ISFIFO(st.st_mode)) {
printf("Piped to another command\n");
} else if (S_ISCHR(st.st_mode)) {
printf("Connected to terminal/character device\n");
}
return 0;
}
Ripgrep Example
Ripgrep automatically adjusts its output:
# Colors and formatting when going to terminal
rg "function" main.c
# Plain text when piped (no ANSI color codes)
rg "function" main.c | head -5
# Override the auto-detection
rg "function" main.c --color=always | head -5 # Force colors
rg "function" main.c --color=never # Force plain text
The difference: terminal output includes ANSI color codes and formatting, while piped output is clean text suitable for further processing.
Bash Detection
#!/usr/bin/env bash
# Basic TTY test
if [ -t 1 ]; then
echo -e "\033[32m✓ Terminal output - using colors\033[0m"
else
echo "✓ Piped/redirected output - plain text"
fi
# Practical usage pattern
if [ -t 1 ]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
else
RED=''
GREEN=''
BLUE=''
NC=''
fi
echo -e "${GREEN}Success:${NC} Operation completed"
echo -e "${RED}Error:${NC} Something went wrong"
[-t 1]
test what is it?
The [-t 1]
breaks down as:
-t
= terminal test (or tty test)1
= file descriptor 1 (which is stdout)
So -t 1
means "test if file descriptor 1 (stdout) is connected to a terminal/TTY."
[ -t 0 ] # Is stdin a terminal?
[ -t 1 ] # Is stdout a terminal?
[ -t 2 ] # Is stderr a terminal?
More on file descriptors in File Descriptor.
Test the Bash Script
# Direct to terminal (shows colors if terminal supports them)
./script.sh
# Piped (plain text output)
./script.sh | cat
# Redirected to file (plain text)
./script.sh > output.txt
Key Points
isatty()
in C and[ -t 1 ]
in bash test if stdout is a terminal- File descriptor 1 = stdout, 0 = stdin, 2 = stderr
- Well-designed CLI tools automatically adapt their output format
- You can usually override auto-detection with flags like
--color=always/never
- This pattern makes tools both human-friendly and script-friendly
Backlinks