Right, so you’ve run a command in your terminal and it’s just sitting there, hanging. Maybe it’s a long-running process, or you just need that shell back. Your first instinct might be to open a new terminal tab. Don’t. That’s like getting up to answer the front door by building a whole new house. The & operator is the elegant, built-in way to get your prompt back without closing shop.

Here’s the magic: you just slap a single ampersand (&) at the end of your command.

$ sleep 30 &
[1] 12345
$

Boom. Instead of staring at a hung terminal for half a minute, you get two things: a job number ([1]) and a Process ID (12345), and most importantly, your prompt back immediately. The process is now running in the background. You’ve just started your first job.

What Just Happened? Jobs vs. Processes

This is where we need a quick but crucial bit of terminology. The shell (bash, zsh, etc.) manages background tasks using a concept called jobs. A job is essentially a unit of work for the shell, which can contain one or more processes. When you use &, you’re creating a background job.

The job number ([1]) is the shell’s internal ID for tracking this unit of work. The PID (12345) is the operating system’s ID for the actual sleep process. You’ll use the job number for shell-specific commands and the PID for OS-level stuff like kill. It’s like the difference between a project code name (the job) and an employee’s ID number (the PID).

The Nasty Little Secret: STDOUT and STDERR

Here’s the first “oh, right, of course” moment. That backgrounded process isn’t attached to your terminal anymore, but it’s still very much running. So what happens when it tries to print something?

$ find /usr -name "*.txt" > output.txt &
[1] 23456
$
# ...a second later...
Hundreds of lines of permission denied errors spam your screen

Surprise! The process’s output (both STDOUT and STDERR) is still, by default, tied to your terminal. It will happily blast its output right into your pristine prompt, interrupting whatever you’re trying to do next. It’s the digital equivalent of your coworker yelling updates from across the room while you’re on a phone call.

The solution is to always, always redirect your output. It’s a non-negotiable best practice.

# The right way: redirect both STDOUT and STDERR
$ find /usr -name "*.txt" > output.txt 2> errors.log &
[1] 23457
$
# Peace and quiet.

You can use &> for a quicker redirect of both streams to the same file, but separating them is usually wiser for debugging.

Reclaiming Your Lost Child: fg and bg

You started a job in the background and now you want to check on it. The jobs command lists all jobs managed by your current shell session.

$ jobs
[1]  - running    sleep 30
[2]  + running    find /usr -name "*.txt" > output.txt 2> errors.log

See that +? It denotes the job that will be acted on if you use a command without a specific job number. The - is the next one in line.

Maybe you backgrounded something and now realize you need to wait for it. You can bring it back to the foreground with fg.

$ fg %1
sleep 30
# Now your terminal is hung again until sleep finishes

Conversely, if you start a command and then decide you want it in the background, you can suspend it with Ctrl+Z (which pauses it) and then resume it in the background with bg.

$ sleep 30
^Z
[1]  + suspended  sleep 30
$ bg
[1]  + continued  sleep 30
$

The Biggest Gotcha: The Shell’s Lifeline

Here’s the critical, “I-wish-someone-had-told-me-that” pitfall. What happens if you close your terminal? Go on, try it. I’ll wait.

Your background jobs are dead. All of them. Why? Because they are children of the shell process. When you close the terminal, the shell terminates, and it sends a SIGHUP (hangup signal) to all its child processes. This is the default behavior and it makes sense for 90% of cases—you generally don’t want processes hanging around after you’ve logged out.

If you do want a job to survive the terminal’s demise, you need to sever this parental link before you close it. You can do this with the disown command, which tells the shell to stop managing the job, or by launching the process with nohup in the first place, which intentionally ignores the SIGHUP signal.

# Launch it immune to hangups from the start
$ nohup ./my_long_script.sh &

# Or, after you've already started it:
$ jobs
[1]  + running    ./my_long_script.sh
$ disown %1

Now, that process is on its own. It’s an orphan, free to wander the system until it completes or is manually killed. Use this power wisely.