How to Use sudo to Redirect Output to a Privileged Location
You try to redirect output to a system file with sudo echo "text" > /etc/config, and you get "Permission denied". What gives? You used sudo!
TL;DR
The redirect (>) is handled by your shell before sudo runs, so it happens with your user permissions, not root. To fix this, use one of these approaches: echo "text" | sudo tee /etc/config, or sudo sh -c 'echo "text" > /etc/config', or sudo bash -c 'echo "text" > /etc/config'. The tee method is usually the simplest and safest.
This is one of the most common stumbling blocks when working with Linux permissions. The key is understanding the order of operations: your shell processes redirects before executing commands.
Let's say you want to add a line to /etc/hosts, which is owned by root. You might try:
sudo echo "127.0.0.1 myapp.local" > /etc/hosts
But you get:
bash: /etc/hosts: Permission denied
Here's what actually happens:
Step 1: Shell sees the redirect '>' and opens /etc/hosts for writing
(This happens as your user, not as root)
Step 2: Shell tries to open /etc/hosts with your permissions
Result: Permission denied
Step 3: sudo echo never runs because step 2 failed
The sudo only applies to the echo command, not the redirect. Your shell opens the output file before sudo even gets involved.
Method 1: Using tee with sudo
The tee command reads from standard input and writes to both standard output and files. When you combine it with sudo, the file writing happens with elevated privileges:
echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts
The -a flag appends to the file instead of overwriting it. Without -a, tee truncates the file first:
# Overwrites /etc/config
echo "new content" | sudo tee /etc/config
# Appends to /etc/config
echo "new line" | sudo tee -a /etc/config
By default, tee also prints the content to stdout. If you don't want to see the output in your terminal, redirect it to /dev/null:
echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts > /dev/null
Notice that the redirect to /dev/null happens in your shell (not as root), but that's fine because anyone can write to /dev/null.
Method 2: Using a Shell with sudo
Another approach is to run a complete shell command with sudo, including the redirect:
sudo sh -c 'echo "127.0.0.1 myapp.local" >> /etc/hosts'
The -c flag tells sh to execute the command string. Now the redirect happens inside the sudo'd shell, so it runs with root privileges.
You can use bash instead of sh if you need bash-specific features:
sudo bash -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf'
This method is useful when you need more complex operations:
sudo bash -c 'echo "# Added by deployment script" >> /etc/config && echo "option=value" >> /etc/config'
Method 3: Using dd
The dd command can also write to files with sudo:
echo "127.0.0.1 myapp.local" | sudo dd of=/etc/hosts oflag=append conv=notrunc
This is more verbose than tee, but it's useful if you need dd's features like block-level control. The options mean:
of=/etc/hosts: Output fileoflag=append: Append mode (like>>)conv=notrunc: Don't truncate the file
For most cases, tee is simpler and more readable.
Practical Example: Updating System Configuration
Let's say you're writing a script to configure a new server. You need to update several system files:
#!/bin/bash
set -e
# Add a custom DNS entry
echo "10.0.0.50 database.internal" | sudo tee -a /etc/hosts > /dev/null
# Configure sysctl settings
echo "net.core.somaxconn = 65535" | sudo tee -a /etc/sysctl.conf > /dev/null
echo "net.ipv4.tcp_max_syn_backlog = 8192" | sudo tee -a /etc/sysctl.conf > /dev/null
# Apply sysctl changes
sudo sysctl -p
echo "System configuration updated"
This script appends to system files safely, and the changes persist across reboots.
Writing Multiple Lines
If you need to write multiple lines, you have several options. Using tee with a here-document:
sudo tee -a /etc/config > /dev/null <<EOF
option1=value1
option2=value2
option3=value3
EOF
Or using sh -c with a here-document:
sudo sh -c 'cat >> /etc/config <<EOF
option1=value1
option2=value2
option3=value3
EOF'
The tee approach is usually cleaner for multi-line content.
Appending vs Overwriting
Be very careful about the difference between > (overwrite) and >> (append).
Overwriting replaces the entire file:
# DANGER: This erases all existing content in /etc/hosts
echo "127.0.0.1 localhost" | sudo tee /etc/hosts
Appending adds to the end:
# Safe: This adds a line to the end of /etc/hosts
echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts
When working with system configuration files, you almost always want to append (-a with tee, or >> with sh -c).
Creating a Backup First
Before modifying system files, create a backup:
# Create a backup with timestamp
sudo cp /etc/hosts /etc/hosts.backup.$(date +%Y%m%d-%H%M%S)
# Then make your change
echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts > /dev/null
For scripts, add backup logic:
#!/bin/bash
set -e
CONFIG_FILE="/etc/myapp.conf"
BACKUP_FILE="${CONFIG_FILE}.backup.$(date +%Y%m%d-%H%M%S)"
# Create backup if file exists
if [ -f "$CONFIG_FILE" ]; then
sudo cp "$CONFIG_FILE" "$BACKUP_FILE"
echo "Backup created: $BACKUP_FILE"
fi
# Now safe to modify
echo "new_option=value" | sudo tee -a "$CONFIG_FILE" > /dev/null
Reading and Writing in One Command
Sometimes you need to read a protected file, modify it, and write it back. You can combine tools:
# Remove commented lines and write back
sudo cat /etc/config | grep -v '^#' | sudo tee /etc/config.new > /dev/null
sudo mv /etc/config.new /etc/config
Or use sed with sudo:
# Replace all occurrences of 'old' with 'new' in a system file
sudo sed -i 's/old/new/g' /etc/config
The -i flag tells sed to edit the file in place, and sudo gives it permission to do so.
Writing to Files in /proc or /sys
The /proc and /sys filesystems expose kernel parameters as files. You can't use normal file editing tools on them, but you can write to them using redirects:
# Enable IP forwarding
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward > /dev/null
# Or with sh -c
sudo sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
These changes take effect immediately but don't persist across reboots. To make them permanent, add them to /etc/sysctl.conf:
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf > /dev/null
Common Mistakes
Here are some things that won't work:
# Won't work - redirect happens before sudo
sudo echo "text" > /etc/config
# Won't work - sudo only applies to cat
sudo cat file.txt > /etc/config
# Won't work - quote issues with sh -c
sudo sh -c echo "text" > /etc/config # The > is outside the quoted command
# Won't work - trying to use sudo inside a redirect
echo "text" > sudo /etc/config
The working versions:
# Works - tee runs with sudo
echo "text" | sudo tee /etc/config
# Works - redirect is inside the sudo'd command
sudo sh -c 'echo "text" > /etc/config'
# Works - cat and redirect both inside sudo'd shell
sudo sh -c 'cat file.txt > /etc/config'
Which Method Should You Use?
For simple cases where you're writing or appending content, use tee:
echo "content" | sudo tee -a /etc/file
For complex operations involving multiple commands or bash features, use sh -c or bash -c:
sudo bash -c 'for i in {1..5}; do echo "line $i" >> /etc/file; done'
For editing files in place, use sed or other editors with sudo:
sudo sed -i 's/pattern/replacement/' /etc/file
Understanding how shells handle redirects before executing commands is key to working with sudo effectively. The tee command is your friend for simple writes, while sh -c or bash -c give you full control for complex operations.
Found an issue?