Advanced Bash Scripting
Streams
Input
Input in shell scripting refers to data fed to a script from the terminal, a file, or another program. In Unix-like systems, every process has three standard streams:
File Descriptor | Stream | Default Device |
---|---|---|
0 | stdin | Keyboard |
1 | stdout | Screen |
2 | stderr | Screen |
What Is Standard Input (stdin)?
Standard input (stdin) uses file descriptor 0. Originally, Unix designers designated stdin as the “inlet” for data streams—primarily from the keyboard.
Types of Input Sources
Broadly, shell scripts receive input in two ways:
- Directly from the terminal (stdin).
- From pipes or files redirected into stdin.
Redirection Operators
Unix provides special operators to reroute these streams:
>
Redirects standard output to a file.<
Feeds a file’s content into stdin.|
Pipes stdout of one command into stdin of another.
Interactive Input with wc
By default, many commands read from stdin if you don’t supply a file. For example, running wc
(word count) without arguments enters interactive mode:
$ wc
Juan Carlos
[Ctrl-D]
1 2 11
Here, wc
reports:
- Lines: 1
- Words: 2
- Characters: 11
Piping Input to wc
More commonly, you chain commands with pipelines:
$ echo "Juan Carlos" | wc
1 2 12
This pipeline connects echo
’s stdout (fd 1) to wc
’s stdin (fd 0).
Detecting Input Source in a Script
You can check whether stdin is a terminal or a pipe/file with test -t 0
:
#!/usr/bin/env bash
if [[ -t 0 ]]; then
echo "Interactive input (keyboard)"
read
else
echo "Input via file or pipeline"
read
fi
Note
The -t
flag returns true if the given file descriptor (e.g., 0 for stdin) is open and refers to a terminal.
Example:
$ ./input_fd.sh
Interactive input (keyboard)
Hello
$ echo "Pipeline" | ./input_fd.sh
Input via file or pipeline
Redirecting from a file also works:
$ wc < input.txt
1 4 24
Testing Other File Descriptors
You can similarly test fd 1 (stdout) or any descriptor:
$ echo "Output" | test -t 1 && echo "Stdout is terminal"
Stdout is terminal
$ echo "Output" | test -t 0 && echo "Stdin is terminal"
# No output—stdin is not a terminal in this case.
Tracing System Calls with strace
Inspect how the shell reads keystrokes in low-level detail:
sudo strace -Tfp $$ 2>&1 | grep -E 'read' &
Typing “hello” produces lines like:
read(0, "h", 1) = 1 <0.00012>
read(0, "e", 1) = 1 <0.00009>
...
Each read
call fetches one byte from stdin. Echoing occurs via stderr (fd 2) on each keystroke; stdout (fd 1) handles command output after Enter.
File Descriptors in the Terminal
In a live terminal session:
- FD 0 reads keystrokes.
- FD 2 echoes keystrokes back.
- FD 1 (stdout) or FD 2 (stderr) displays command results.
Capstone Exercise: Using a Custom File Descriptor
Practice redirection with an extra descriptor (fd 3). Fix an email typo in email_file.txt
by replacing a space with a dot:
# Create file with typo
echo "Juan [email protected]" > email_file.txt
# Open fd 3 for read/write on the file
exec 3<> email_file.txt
# Read first 4 characters ("Juan")
read -n 4 <&3
# Write a dot at current offset
echo -n "." >&3
# Close fd 3
exec 3<&-
exec 3>&-
# Verify
cat email_file.txt
# Output: [email protected]
This demonstrates how custom file descriptors can modify files in place.
We’ve covered interactive stdin, pipes, redirection, descriptor testing, and system-call tracing. Next, explore Here Documents (Heredocs) for multiline input blocks.
References
- Bash Manual: Redirecting Input and Output
- strace Documentation
- Unix File Descriptors
- Advanced Bash-Scripting Guide
Watch Video
Watch video content
Practice Lab
Practice lab