# Shell I/O and Redirection ## Redirection Redirect standard input, output, and error between files and file descriptors. File descriptor 1 is stdout, 2 is stderr, 0 is stdin. - `>` — Write stdout to file (truncates if exists) - `>>` — Append stdout to file - `<` — Read stdin from file - `2>` — Redirect stderr to file - `&>` — Redirect both stdout and stderr ```bash # Redirect output to file ls > files.txt # Append to file echo "more content" >> files.txt # Redirect both stdout and stderr command > output.txt 2>&1 # Redirect stderr only command 2> errors.log # Discard output command > /dev/null 2>&1 # Read input from file sort < unsorted.txt ``` Combine redirections to separate stdout and stderr streams: ```bash # Stdout goes to output.txt, stderr to error.txt script.sh > output.txt 2> error.txt # Send stderr to stdout script.sh 2>&1 # Send stderr to stdout, then pipe to grep script.sh 2>&1 | grep "error" ``` ## Pipelines and tee Pipe the stdout of one command into the stdin of another with `|`. Chain multiple commands to transform data. ```bash # Count lines in a file cat myfile.txt | wc -l # Chain commands: list, sort, count unique ls | sort | uniq | wc -l # Find large files and sort by size find . -type f -exec ls -lh {} \; | sort -k5 -h ``` Use `tee` to write to a file **and** stdout simultaneously. Useful for logging intermediate pipeline output. ```bash # Capture output to file while still printing it cat data.txt | tee backup.txt | grep "important" # Append mode with tee command | tee -a logfile.txt # Tee in a pipeline with multiple commands generate_report | tee report.txt | mail -s "Report" admin@example.com ``` ## Command Substitution Run a command and substitute its output in-place with `$(command)` or `` `command` ``. The `$()` syntax is preferred (nestable, more readable). ```bash # Assign command output to variable files=$(ls *.txt) count=$(wc -l < data.txt) # Use in string echo "Found $(ls -1 | wc -l) files" # Nested command substitution (only works with $(), not backticks) total=$(( $(wc -l < file1.txt) + $(wc -l < file2.txt) )) ``` Be careful with whitespace: command substitution removes trailing newlines but preserves embedded whitespace. Quote variables to preserve spaces and newlines. ```bash # Without quotes: loses structure for file in $(ls) # Splits on whitespace, breaks with spaces in names echo $file done # With quotes: preserves structure for file in $(ls) do echo "$file" # Correctly handles spaces and newlines done # Alternative: use -print0 with while read ls -print0 | while IFS= read -rd '' file; do echo "$file" done ```