Skip to content

Job Control — Forground and Background


So far, you’ve been controlling processes by PID and signal. Job control deals with a slightly different situation: the relationship between a command you start from the shell and the terminal that started it.

When you run a command normally, the shell waits for it in the foreground. You can instead put it in the background, move it between foreground and background, or start it so that it can survive after the terminal session ends.

Foreground Jobs

Run a command normally:

sleep 30

Your shell is now waiting for sleep to finish.

You cannot enter another shell command in that terminal until it exits:

Shell
  │
  └── starts sleep
          │
          │ foreground
          ▼
      terminal is occupied

This is a foreground job. The shell has given the terminal’s foreground control to that job and waits for it to complete.

Sending A Foreground Job To The Background

You can suspend a running foreground job with:

Ctrl+Z

For example:

sleep 300

Press:

Ctrl+Z

You should see something similar to:

[1]+  Stopped  sleep 300

The process has not exited. It has been stopped. The shell now has your terminal back. You can see the shell’s known jobs with:

jobs

For example:

[1]+  Stopped  sleep 300

The number in brackets is the job ID assigned by this shell.

This is different from the process’s PID.

Job ID
    ↓
shell's view of the job

PID
    ↓
kernel's identifier for the process

bg: Continue A Job In The Background

After pressing Ctrl+Z, the job is stopped.

To continue it in the background:

bg

You might see:

[1]+ sleep 300 &

Now the shell is free again and the command continues running.

Check:

jobs

and you may see:

[1]+  Running  sleep 300 &

The important transition is:

foreground
    ↓ Ctrl+Z
stopped
    ↓ bg
background

The process was not restarted. It was stopped and then continued.

Important (Don’t Skip)

sleep might behave differently as it internally calls linux kernel nanosleep() system call which works regardless of app’s state (running/stopped) but when app is stopped it can’t exit which means it does that only after you unpause it. See nanosleep man page and this Stackoverflow answer for more details.

So, even if you send signal — app should still be designed to catch that. Most applications do but some might not. sleep is just choosen for simplicity. Use larger N seconds (for eg: sleep 300 or even more) so that you can see things happening even though sleep is not behaving internally the same.

Start A Command Directly In The Background

You don’t have to start in the foreground and then move the job.

Add & to the command:

sleep 300 &

Note

Be aware that the semicolon after & as such &; is invalid syntax:

  • & already terminates/backgrounds the command.
  • ; is a separate command separator, so &; is invalid syntax.

The shell immediately returns your prompt:

[1] 4821
$

Here:

[1]
    job ID

4821
    PID

The command is running in the background.

You can verify it with:

jobs

or inspect the PID using the process tools you’ve already learned:

ps -p 4821 -o pid,stat,cmd

This demonstrates the relationship between shell job control and normal process management:

Shell
  │
  ├── job ID 1
  │
  └── PID 4821
          │
          ▼
       Process

The job ID belongs to the shell’s job-control system. The PID belongs to the Linux process system.

fg: Bring A Background Job Forward

Suppose:

jobs

shows:

[1]+  Running  sleep 300 &

Bring it back to the foreground:

fg

The shell waits for it again.

You can also specify a particular job:

fg %1

The % tells the shell that 1 is a job ID rather than a PID.

So:

fg %1

means:

Bring job number 1 into the foreground.

jobs, bg, And fg

These commands operate on the shell’s own jobs:

CommandPurpose
jobsShow jobs known to the current shell
bgContinue a stopped job in the background
fgBring a job into the foreground

A useful flow is:

sleep 300
     │
     │ Ctrl+Z
     ▼
  Stopped
     │
     │ bg
     ▼
 Background
     │
     │ fg
     ▼
 Foreground

This is job control.

What Does Ctrl+C Do?

You’ve probably already used:

Ctrl+C

to stop a command.

This is different from:

Ctrl+Z

Ctrl+C normally causes the terminal to send SIGINT to the foreground process group.

Ctrl+Z normally causes the terminal to send SIGTSTP, which stops the foreground job.

So:

Ctrl+C
    → interrupt the foreground job

Ctrl+Z
    → stop the foreground job

The process-control distinction is important.

With Ctrl+C, the usual intention is:

"Stop this command."

With Ctrl+Z, the intention is:

"Pause this command; I'll decide what to do with it."

After Ctrl+Z, you can use:

bg

to continue it in the background or:

fg

to bring it back.

What Happens To Terminal Output?

Background processes can still write to the terminal.

For example:

while true; do
    echo "background process"
    sleep 2
done &

The shell gives you your prompt, but the background process can continue printing:

$ background process
background process
background process

This can make the terminal difficult to use. Job control does not mean that a background process becomes completely detached from the terminal. It simply means the shell isn’t waiting for it as the foreground job.

Running command in background (with &) only detaches stdin so that you can run other commands. But stdout and stderr are still attached to the parent shell. This is the reason you keep seeing background process output scattered your terminal. For More see here…

Tip

If you don’t want to see stdout and/or stderr you can just redirect them to a file or to /dev/null. For the above example you can:

while true; do
    echo "background process" > output.txt
    sleep 2
done &

Now output.txt will be scattered with the output, not your terminal till you kill this long running loop or the shell itself.

Standard Input Is Different

A background process that tries to read from the terminal can be stopped because it does not own the terminal’s foreground input.

This is another reason that “background” does not mean “independent of the terminal.”

For simple commands such as:

sleep 300 &

This isn’t a problem because sleep doesn’t need input. For interactive programs, foreground/background behavior matters much more. This means you really shouldn’t do read & which doesn’t make sense in most of the cases.

nohup: Survive A Terminal Disconnect

Backgrounding a process does not automatically make it survive when you close the terminal.

For example:

long-running-command &

is still associated with the shell and terminal session.

If the shell exits or the terminal connection disappears, the process may receive SIGHUP depending on how it was started and how the shell handles its jobs.

This is where nohup is useful.

nohup long-running-command &

nohup means no hangup.

It starts the command in a way intended to protect it from the terminal hangup signal.

For example:

nohup sleep 300 &

You can then close the terminal without relying on the shell’s normal interactive job-control behavior to keep the command alive. This means sleep 300 & will still be running in backgroud even if you close your terminal.

Where Does Output Go With nohup?

If you run:

nohup sleep 300 &

there is no interesting output to save because sleep doesn’t print anything.

For a command that produces output:

nohup ./backup.sh &

nohup normally redirects output that would otherwise go to the terminal to a file named:

nohup.out

You can explicitly choose your own output file instead:

nohup ./backup.sh > backup.log 2>&1 &

Now:

stdout → backup.log
stderr → backup.log

This is often more useful for a long-running command because you know exactly where its output is going. For demonstrative purpose:

nohup echo "See Me in myoutput.txt" > myoutput.txt 2>/dev/null & wait; cat myoutput.txt

nohup Is Not A Process Manager

It is worth keeping the scope clear. nohup is useful for a command you want to continue after the terminal session goes awayIt is not a replacement for a proper service manager. For example, if you need a program to:

start automatically at boot
restart after failure
maintain logs
run as a specific service account

That is a different problem.

nohup solves the much smaller problem:

“I started this command interactively, and I don’t want a terminal hangup to stop it.”

disown: Remove A Job From The Shell

Some shells also provide:

disown

This removes a job from the shell’s job table.

For example:

long-running-command &

then:

jobs

shows the job.

You can remove it from the shell’s job list with:

disown %1

After that, the current shell no longer considers it one of its jobs.

A common pattern is:

long-running-command &
disown

This is related to nohup, but they solve different parts of the problem.

nohup
    → protects the command from SIGHUP

disown
    → removes the job from the shell's job table

They are sometimes used together when you want a command started from an interactive shell to become independent of that shell.

Job ID vs PID

This distinction is worth keeping straight.

Suppose:

sleep 300 &

prints:

[1] 4821

Then:

1
    → job ID in this shell

4821
    → process ID in Linux

So these are different:

fg %1

uses the shell’s job ID.

Whereas:

kill 4821

uses the kernel’s PID.

You can use both systems at the same time:

Shell job control
      │
      ├── %1
      │
      ▼
    PID 4821
      │
      ▼
Linux process

A Small Practical Workflow

Start a command:

sleep 300

Suspend it:

Ctrl+Z

Check it:

jobs

Continue it in the background:

bg

Bring it back:

fg

You can also start directly in the background:

sleep 300 &

And for a command that should survive a terminal disconnect:

nohup ./backup.sh > backup.log 2>&1 &

These are simple commands, but they cover most of the everyday shell job-control workflow.

What You Should Remember

There are three related but different concepts:

Foreground
    → shell gives the job the terminal and waits for it

Background
    → shell gives you the prompt while the job continues

Detached
    → job is no longer dependent on the interactive shell/terminal

The basic commands are:

Ctrl+Z
    → stop foreground job

bg
    → continue stopped job in background

fg
    → bring background job to foreground

jobs
    → inspect jobs belonging to this shell

nohup
    → protect a command from terminal hangup

disown
    → remove a job from the shell's job table

The key distinction is that backgrounding is not the same thing as detaching. A command started with & can still be tied to the terminal session, while nohup and disown address different parts of that relationship.

Last updated on