Advanced Fish: Process Management Techniques
What It Is
Fish (Friendly Interactive Shell) is a modern, user‑friendly Unix shell that offers powerful process management capabilities. Process management in Fish refers to the ability to create, monitor, control, and communicate with running programs (processes) from within the shell. Unlike simple command execution, advanced process management involves techniques such as backgrounding, job control, process substitution, signal handling, and orchestration of multiple concurrent processes. Fish provides a rich set of built‑in commands and syntax to make these tasks intuitive and efficient.
Key components of Fish’s process management include:
- Job control – managing foreground and background jobs
- Process substitution – using output of one process as a file argument
- Pipes and redirections – connecting processes and controlling I/O
- The
execcommand – replacing the shell with another process - Signal handling – sending signals like SIGTERM, SIGKILL, etc.
- The
waitcommand – synchronizing with background processes - Command groups and subshells – running multiple commands in a controlled environment
Why It Matters
Mastering process management is crucial for any developer who works in a terminal environment. Efficient use of these techniques leads to:
- Increased productivity – run long‑running tasks in the background while continuing to work.
- Better resource utilization – parallel execution speeds up batch processing.
- Reliable scripting – properly handle process lifecycles, avoid zombies, and manage errors.
- Complex workflows – chain processes, substitute outputs, and create pipelines that process data on the fly.
- Cleaner code – Fish’s syntax reduces boilerplate compared to traditional shells like Bash.
Whether you are building a deployment script, a data pipeline, or a simple automation tool, understanding Fish’s process management techniques will make you more effective and your scripts more robust.
How to Use It
Job Control: Backgrounding & Foregrounding
Fish makes backgrounding a process trivial by appending an ampersand (&) to a command. The process runs in the background and returns control to the shell immediately.
# Run a long task in the background
long_running_task &
# Output: [1] 12345
The shell prints a job number (in brackets) and a PID. You can manage background jobs with the built‑ins fg (bring to foreground), bg (resume a stopped background job), and jobs (list jobs).
# List all background jobs
jobs
# Output:
# Job Group State Command
# 1 12345 running long_running_task
# Bring job 1 to the foreground
fg 1
# Suspend a foreground job with Ctrl+Z, then send it to background
bg %1
Fish allows you to refer to jobs by their job number with the % prefix. This is consistent and predictable.
Process Substitution
Fish supports process substitution using (command) – the output of the command is captured as a temporary file (or a named pipe on systems that support it). This is extremely useful when you need to pass the result of a command as a file argument to another command.
# Compare the output of two commands as if they were files
diff (command1 | sort) (command2 | sort)
Fish automatically creates temporary files or FIFOs and cleans them up. You can also use process substitution with standard input:
# Use output of a command as input to another
cat (ls *.txt) | wc -l
Unlike Bash’s <(...) syntax, Fish uses (...) which is already familiar from command substitution. The shell detects when a substitution is used in a context where a file path is expected and handles it accordingly.
Piping and Redirection
Fish follows standard Unix piping (|) and redirection (>, >>, <), but adds some convenient extensions. You can redirect both stdout and stderr simultaneously using &> or ^&1 for merging.
# Redirect stdout and stderr to the same file
command &> output.log
# Redirect stderr to stdout (merge streams)
command 2>&1
# Append stderr to a file
command 2>> error.log
Fish also supports heredocs and herestrings for inline input:
# Here string: send a string as stdin
grep "error" <<< "no error found"
# Here doc: multi‑line input
cat << EOF
Line 1
Line 2
EOF
Using exec
The exec command replaces the current shell process with a new command. This is useful for resource‑sensitive scripts or when you want to avoid leaving a parent shell behind.
# Replace the shell with a Python REPL
exec python3
# After this command, the shell is gone; you are in Python.
# To return, you must exit Python.
In scripts, exec is often used to redirect all subsequent output without creating subshells:
# Redirect all output of the script to a log file
exec > script.log 2>&1
echo "This goes to script.log"
date
Process IDs and Signals
Fish provides the %self variable for the current shell’s PID and $last_pid for the last backgrounded process. You can send signals with the kill command (or signal in Fish).
# Background a process and capture its PID
sleep 100 &
set bg_pid $last_pid
# Send SIGTERM to the background process
kill -15 $bg_pid
# Send SIGKILL (force kill)
kill -9 $bg_pid
Fish also allows you to send signals by job number:
# Kill job 1
kill %1
# Send SIGSTOP to job 2
kill -STOP %2
To handle signals in Fish scripts, you can use the trap command (built‑in):
function cleanup
echo "Cleaning up..."
rm -f /tmp/tempfile
end
trap cleanup SIGINT SIGTERM
# Now if the script receives Ctrl+C, cleanup runs.
sleep 100
Using wait
The wait command blocks the shell until all background jobs complete. You can also wait for