Job Control in Bash: fg, bg und disown im Detail
AI generated
$_
#!/
Bash · Job Control · Prozessverwaltung · Linux
Job Control in Bash: fg, bg und disown im Detail
von Ctrl-Z bis zur sauberen Trennung vom Terminal

Job Control ist weit mehr als Ctrl-Z und ein schnelles bg danach. Wer versteht, wie Bash Jobs, Prozessgruppen und Signale verwaltet, kann lange Migrationen, Backups und Wartungsarbeiten sicher im Hintergrund laufen lassen, ohne dass eine getrennte SSH-Sitzung den ganzen Fortschritt mit sich reisst.

16 Min. Lesezeit jobs · fg · bg · disown · SIGHUP Bash 4.x · 5.x · Linux

1. Was Job Control in Bash wirklich bedeutet

Job Control ist die Fähigkeit einer interaktiven Shell, mehrere Prozesse gleichzeitig zu starten, zwischen ihnen zu wechseln und sie unabhängig vom aktuellen Terminal weiterlaufen zu lassen. Wer in einer SSH-Sitzung ein langes Backup startet, es mit Ctrl-Z anhält, mit bg im Hintergrund weiterlaufen lässt und danach die Verbindung trennt, nutzt Job Control, meist ohne die dahinterliegende Mechanik zu kennen. Genau diese Mechanik entscheidet aber darüber, ob der Prozess die Trennung überlebt oder mit der Sitzung stirbt.

Intern verwaltet Bash für jede interaktive Sitzung eine Jobtabelle. Jeder gestartete Hintergrundprozess bekommt eine Jobnummer, eine Prozessgruppen-ID und einen Status: laufend, angehalten oder beendet. Job Control ist damit ein eigenes Subsystem der Shell, das auf POSIX-Prozessgruppen und Terminal-Steuerung aufsetzt, nicht nur eine Sammlung einzelner Befehle. Nur wenn man versteht, dass hinter fg, bg und disown eine gemeinsame Datenstruktur steht, ergeben die Feinheiten im nächsten Abschnitt Sinn.

In Skripten ist Job Control standardmäßig deaktiviert, weil nicht interaktive Shells keine Terminal-Kontrolle besitzen. Das erklärt, warum Befehle wie fg oder bg in einem via cron gestarteten Skript ohne explizites set -m scheitern. Für Administratoren, die produktive Wartungsfenster planen, ist das Verständnis dieser Grenze zwischen interaktiver und nicht interaktiver Job Control der erste Schritt zu robusten Abläufen.

2. Jobs anzeigen und referenzieren: jobs und %n

Der Befehl jobs zeigt alle aktiven Hintergrund- und angehaltenen Prozesse der aktuellen Shell an, jeweils mit Jobnummer, Status und dem ursprünglichen Kommando. Diese Übersicht ist der Ausgangspunkt jeder Job Control-Operation, denn ohne die Jobnummer lässt sich kein Prozess gezielt in den Vordergrund oder Hintergrund holen. Mit jobs -l wird zusätzlich die Prozess-ID angezeigt, was für die Kombination mit kill oder disown -h wichtig ist.

Jobs werden per %n referenziert, wobei n die Jobnummer aus der Tabelle ist. %+ oder %% verweist immer auf den zuletzt aktiven Job, %- auf den vorletzten. Diese Kurzschreibweisen sind ein zentraler Teil der Job Control-Syntax und ersparen es, ständig die volle Jobnummer nachzuschlagen. Ein Job kann außerdem über einen Teilstring seines Startkommandos referenziert werden, etwa %rsync für den zuletzt gestarteten Job, dessen Kommando mit rsync beginnt.


# Start three background jobs to inspect job control state
sleep 300 &
rsync -av /data/ backup-host:/data/ &
tail -f /var/log/syslog &

jobs -l
# [1]  12345 Running    sleep 300 &
# [2]- 12346 Running    rsync -av /data/ backup-host:/data/ &
# [3]+ 12347 Running    tail -f /var/log/syslog &

# Reference jobs by number, by +/- shortcut, or by command prefix
fg %1        # bring job 1 to foreground
kill %rsync  # send SIGTERM to the job whose command starts with rsync
wait %2      # block until job 2 finishes, capture its exit code

Der + und der Minus-Marker in der jobs-Ausgabe sind kein kosmetisches Detail. Sie bestimmen, welcher Job gemeint ist, wenn man fg oder bg ohne Argument aufruft. Wer regelmäßig mit mehreren parallelen Hintergrundprozessen arbeitet, sollte sich angewöhnen, immer die explizite Jobnummer zu verwenden, denn der aktuelle Job kann sich ändern, sobald ein anderer Job endet oder in den Vordergrund geholt wird.

3. Zwischen Vordergrund und Hintergrund wechseln: fg und bg

fg holt einen angehaltenen oder im Hintergrund laufenden Job in den Vordergrund und verbindet ihn wieder mit dem Terminal, sodass Ein- und Ausgabe direkt sichtbar werden. bg setzt einen angehaltenen Job fort, lässt ihn aber im Hintergrund weiterlaufen, ohne die Shell zu blockieren. Diese beiden Befehle sind die operative Seite von Job Control, während jobs nur die Beobachtungsseite abdeckt.

Ein häufiger Anwendungsfall: Ein interaktives Kommando läuft länger als erwartet, man drückt Ctrl-Z, um es anzuhalten, prüft mit jobs den Status, und schickt es dann mit bg %1 in den Hintergrund weiter. Die Shell steht sofort wieder für neue Eingaben zur Verfügung, während der Prozess unbeeinflusst weiterläuft. Dieses Muster aus Job Control-Befehlen ersetzt in vielen Fällen das vorausschauende Anhängen eines & beim ursprünglichen Aufruf.


# Start a long-running compression job in the foreground
tar -czf backup.tar.gz /var/www

# Press Ctrl-Z to suspend it, then check the job table
# ^Z
# [1]+  Stopped    tar -czf backup.tar.gz /var/www

jobs
# [1]+  Stopped    tar -czf backup.tar.gz /var/www

# Resume it in the background, freeing up the shell
bg %1
# [1]+ tar -czf backup.tar.gz /var/www &

# Later, bring it back to the foreground to watch it finish
fg %1

Ein Detail, das viele übersehen: bg funktioniert nur auf angehaltenen Jobs, nicht auf beendeten. Ruft man bg auf einen Job auf, der bereits abgelaufen ist, meldet Bash einen Fehler. Ebenso liefert fg auf einen bereits im Vordergrund laufenden Job keinen sinnvollen Effekt, weil Job Control pro Terminal jeweils nur einen Vordergrundprozess kennt. Diese Grenzfälle sind selten dokumentiert, verursachen in der Praxis aber Verwirrung bei Einsteigern.

4. Ctrl-Z, SIGTSTP und SIGCONT im Detail

Hinter Ctrl-Z steckt das Signal SIGTSTP, das der Terminal-Treiber an die aktuelle Vordergrund-Prozessgruppe sendet. SIGTSTP unterscheidet sich fundamental von SIGTERM oder SIGKILL: Es beendet den Prozess nicht, sondern versetzt ihn in den gestoppten Zustand, aus dem er jederzeit fortgesetzt werden kann. Job Control baut komplett auf dieser Stopp-und-Fortsetzen-Mechanik auf, nicht auf dem Beenden und Neustarten von Prozessen.

Fortgesetzt wird ein gestoppter Prozess über SIGCONT, das sowohl fg als auch bg intern an die Prozessgruppe senden. Der Unterschied zwischen beiden Befehlen liegt allein darin, ob die Prozessgruppe danach die Terminal-Kontrolle zurückbekommt (fg) oder nicht (bg). Dieses Zusammenspiel aus SIGTSTP und SIGCONT ist der Kern dessen, was Job Control von einfachem Prozessmanagement unterscheidet, denn es erlaubt das nahtlose Pausieren und Fortsetzen ganzer Prozessgruppen inklusive aller Kindprozesse.

Ein Prozess kann sich außerdem selbst gegen SIGTSTP immunisieren, etwa Datenbank-Server, die während eines Commits nicht unterbrochen werden wollen. Für Administratoren bedeutet das: Ctrl-Z ist kein garantiertes Anhalten, sondern eine Anfrage, die der Zielprozess ignorieren kann. Diese Nuance ist besonders relevant, wenn Job Control in Skripten zur Steuerung länger laufender Datenbank-Migrationen eingesetzt wird.

5. disown: Jobs von der Shell lösen ohne sie zu beenden

disown entfernt einen Job aus der Jobtabelle der aktuellen Shell, ohne den Prozess selbst zu beenden. Das ist der entscheidende Unterschied zu kill: Der Prozess läuft unverändert weiter, aber die Shell verliert jede Kontrolle und jedes Wissen über ihn. Innerhalb von Job Control ist disown das Werkzeug, um einen Hintergrundprozess bewusst vom Lebenszyklus der aktuellen Sitzung zu entkoppeln, bevor man die Sitzung beendet.

Ohne disown erhält jeder Hintergrundjob beim Beenden der interaktiven Shell standardmäßig SIGHUP, sofern huponexit gesetzt ist oder die Verbindung tatsächlich getrennt wird. Das Signal SIGHUP beendet den Prozess in den meisten Fällen sofort, weil er kein eigenes Handling dafür implementiert. disown -h markiert einen Job speziell so, dass er von SIGHUP ausgenommen wird, aber weiterhin in der Jobtabelle sichtbar bleibt, ein nützlicher Mittelweg zwischen vollem disown und keiner Absicherung.


# Start a long migration job that must survive session disconnect
./run-migration.sh &
migration_pid=$!

# Fully detach the job from the shell's job table
disown %1

# Alternative: keep it visible in "jobs" but exempt from SIGHUP
# disown -h %1

# Verify detachment: the job no longer appears in the job table
jobs
# (no output for the disowned job)

# The process still exists at the OS level and can be checked directly
ps -p "$migration_pid" -o pid,ppid,stat,cmd

disown ohne Argument wirkt auf den aktuellen Job, disown -a auf alle Jobs der Sitzung, disown -r nur auf laufende, nicht auf gestoppte Jobs. Diese Varianten sind besonders in Wartungsskripten hilfreich, in denen mehrere Hintergrundprozesse gleichzeitig gestartet werden und alle die Trennung der SSH-Sitzung überstehen sollen. Job Control mit gezieltem disown ersetzt in solchen Fällen den Griff zu tmux oder screen, wenn die zusätzliche Komplexität eines Terminal-Multiplexers nicht nötig ist.

6. nohup, disown und setsid im Vergleich

nohup, disown und setsid lösen ein ähnliches Problem auf unterschiedlichen Ebenen, und die Wahl des richtigen Werkzeugs hängt vom genauen Bedürfnis ab. nohup wird beim Start eines Befehls vorangestellt und sorgt dafür, dass der neue Prozess SIGHUP von vornherein ignoriert, unabhängig davon, ob er später jemals in der Job Control-Tabelle der Shell auftaucht. disown hingegen wirkt nachträglich auf einen bereits laufenden Job und verändert dessen Sichtbarkeit in der Shell selbst.

setsid geht noch einen Schritt weiter: Es startet den Prozess in einer komplett neuen Sitzung mit eigener Prozessgruppen-ID, losgelöst vom kontrollierenden Terminal von Anfang an. Während disown nur die Bash-interne Jobtabelle bereinigt, kappt setsid die Verbindung auf Kernel-Ebene. Für echte Daemon-Prozesse, die dauerhaft und unabhängig vom Start-Terminal laufen sollen, ist setsid deshalb robuster als reines Job Control mit disown, weil auch ein erneutes Anhängen an das Terminal keinen Einfluss mehr hat.


# nohup: ignore SIGHUP from the very start, redirect output explicitly
nohup ./long-report.sh > report.log 2>&1 &

# disown: detach an already-running job control entry after the fact
./long-report.sh &
disown -h %1

# setsid: fully detach into a new session, independent of any terminal
setsid ./long-report.sh > report.log 2>&1 < /dev/null &

# Combine both for maximum robustness against terminal loss
setsid nohup ./long-report.sh > report.log 2>&1 < /dev/null &

7. Job Control in nicht interaktiven Skripten

Standardmäßig ist Job Control in nicht interaktiven Bash-Skripten deaktiviert, auch wenn das Skript selbst Hintergrundprozesse mit & startet. Der Befehl jobs funktioniert zwar meist noch, aber fg und bg schlagen mit der Meldung no job control in this shell fehl. Wer explizit interaktive Job Control-Semantik in einem Skript benötigt, muss set -m an den Anfang setzen, was jeden Hintergrundprozess in eine eigene Prozessgruppe stellt.

In den meisten Automatisierungsskripten ist set -m allerdings nicht der richtige Ansatz, weil man selten interaktiv zwischen Jobs wechseln will. Stattdessen genügt es, PIDs mit $! zu erfassen und mit wait gezielt auf sie zu warten, wie im nächsten Artikel dieser Serie zu wait -n vertieft wird. Das eigentliche Ziel in Skripten ist meist nicht Job Control im interaktiven Sinne, sondern zuverlässiges Prozessmanagement ohne Terminal-Bindung.


#!/usr/bin/env bash
set -euo pipefail

# Job control is off by default in non-interactive scripts.
# "fg"/"bg" would fail here without "set -m" first.
./worker-a.sh &
pid_a=$!
./worker-b.sh &
pid_b=$!

# The script-friendly equivalent of job control: track PIDs, wait explicitly
wait "$pid_a"
echo "worker-a exit code: $?"
wait "$pid_b"
echo "worker-b exit code: $?"

8. Typische Fehler beim Umgang mit Job Control

Der häufigste Fehler ist die Annahme, ein & am Zeilenende reiche aus, damit ein Prozess eine SSH-Trennung übersteht. Ohne disown oder nohup erhält der Hintergrundjob beim Sitzungsende SIGHUP und stirbt meist sofort. Wer Job Control für lange Wartungsfenster einsetzt, muss diese Absicherung explizit einbauen, bevor die Verbindung getrennt wird, nicht danach, denn dann ist es zu spät.

Ein zweiter Fehler betrifft das Verwechseln von %1 mit der Prozess-ID. Die Jobnummer %1 ist nur innerhalb der Shell gültig, die den Job gestartet hat, während die tatsächliche PID systemweit eindeutig ist. Wer disown %1 in einer neuen Shell-Sitzung eingibt, in der der Job gar nicht in der Jobtabelle steht, erhält einen Fehler, weil dort keine Job Control-Referenz existiert. Ein dritter Fehler: bg auf einen Job anwenden, der stdin liest, ohne die Eingabe umzuleiten. Der Prozess bleibt dann bei Bedarf stumm angehalten (SIGTTIN), was in Logs schwer zu erkennen ist.

9. Job-Control-Werkzeuge im direkten Vergleich

Die folgende Tabelle stellt die wichtigsten Werkzeuge rund um Job Control gegenüber, sortiert nach dem Grad der Entkopplung vom aktuellen Terminal, den sie erreichen.

Werkzeug Wirkung Übersteht SSH-Trennung Typischer Einsatz
& allein Hintergrundprozess in Jobtabelle Nein Kurze Aufgaben in derselben Sitzung
bg / fg Statuswechsel eines Jobs Nein Interaktives Pausieren und Fortsetzen
disown Job aus Jobtabelle entfernen Ja (bei -h auch SIGHUP-fest) Nachträgliches Absichern laufender Jobs
nohup SIGHUP-Ignorierung ab Start Ja Von Anfang an geplante Langläufer
setsid Neue Sitzung, kein Terminal Ja, vollständig Echte Daemon-Prozesse

Für die meisten Wartungsfenster reicht disown -h nach dem Start des Prozesses völlig aus, weil die Prozessgruppe unverändert bleibt und der Job in jobs weiterhin sichtbar ist. setsid lohnt sich erst, wenn der Prozess dauerhaft als Daemon laufen soll und keine Terminal-Bindung mehr existieren darf, ein Thema, das im Artikel zum Betrieb von Bash-Skripten als Daemon vertieft wird.

Mironsoft

Shell-Automatisierung, DevOps-Tooling und Deployment-Infrastruktur

Wartungsfenster, die keine SSH-Verbindung mehr brauchen?

Wir bauen Job Control, disown-Absicherung und Daemon-Betrieb sauber in eure Bash-Automatisierung ein, damit lange Migrationen, Backups und Wartungsskripte auch bei getrennter Verbindung zuverlässig weiterlaufen.

Skript-Review

Job Control und SIGHUP-Absicherung in bestehenden Skripten prüfen

Daemon-Betrieb

setsid, systemd-Units und Neustart-Strategien einrichten

Wartungsfenster

Migrationen und Backups robust gegen Verbindungsabbrüche machen

10. Zusammenfassung

Job Control in Bash ist ein eigenständiges Subsystem, das Prozessgruppen, Signale wie SIGTSTP und SIGCONT sowie eine sitzungsgebundene Jobtabelle kombiniert. jobs zeigt den Zustand, fg und bg wechseln zwischen Vorder- und Hintergrund, und disown löst einen Job endgültig von der Shell, ohne ihn zu beenden. Wer diese Bausteine kennt, kann lange Wartungsarbeiten gezielt gegen Verbindungsabbrüche absichern, statt sich auf Zufall zu verlassen.

Für dauerhafte Prozesse, die unabhängig vom Start-Terminal existieren sollen, reicht reines Job Control mit disown oft nicht aus, hier ist setsid oder ein systemd-Unit die robustere Wahl. Die Kombination aus nohup beim Start und disown -h nachträglich deckt aber die meisten praktischen Fälle in Backup-, Migrations- und Wartungsskripten zuverlässig ab.

Job Control mit fg, bg und disown — Das Wichtigste auf einen Blick

Jobs referenzieren

jobs -l zeigt alle Hintergrundprozesse. %n, %+ und %- referenzieren sie eindeutig für fg, bg und kill.

Signale dahinter

Ctrl-Z sendet SIGTSTP, fg und bg setzen mit SIGCONT fort. Der Prozess selbst wird dabei nie beendet.

disown vs. nohup

disown wirkt nachträglich auf laufende Jobs, nohup von Anfang an. Beides schützt vor SIGHUP bei Sitzungsende.

In Skripten

Nicht interaktive Skripte kennen Job Control nur mit set -m. Meist genügt $! plus wait für zuverlässiges Prozessmanagement.

11. FAQ: Job Control in Bash

1Was ist Job Control in Bash genau?
Das Subsystem der interaktiven Shell, das Hintergrundprozesse in einer Jobtabelle verwaltet und zwischen Vordergrund und Hintergrund über Prozessgruppen und Signale wechselt.
2Sendet Ctrl-Z den Prozess in den Hintergrund?
Nein, Ctrl-Z sendet nur SIGTSTP und hält an. Erst bg setzt den Job im Hintergrund fort.
3disown vs. kill: was ist der Unterschied?
disown entfernt den Job nur aus der Jobtabelle, der Prozess läuft weiter. kill sendet ein Signal und kann ihn beenden.
4Übersteht & allein eine SSH-Trennung?
Meist nicht. Ohne disown oder nohup erhält der Job SIGHUP beim Sitzungsende und wird beendet.
5disown vs. disown -h?
disown entfernt vollständig, disown -h bleibt sichtbar in jobs, ist aber gegen SIGHUP immun.
6Wann setsid statt disown?
Für echte Daemon-Prozesse ohne jede Terminal-Bindung. setsid startet eine neue Sitzung von Anfang an.
7Warum funktioniert fg im Cron-Skript nicht?
Cron-Shells sind nicht interaktiv, Job Control ist deaktiviert. Ohne set -m schlägt fg fehl.
8disown aus anderer Sitzung möglich?
Nein, die Jobtabelle ist an die startende Shell gebunden. Von außen nur über die PID mit kill erreichbar.
9Was bedeutet Stopped (SIGTTIN)?
Der Hintergrundjob will von stdin lesen, hat aber keine Terminal-Kontrolle und wird deshalb automatisch angehalten.
10Wirkt disown -a auf alle Jobs?
Ja, disown -a auf alle Jobs. disown -r beschränkt sich auf laufende Jobs, gestoppte bleiben unberührt.