Het super-tip van de opdracht fg : wanneer Ctrl+Z je terminalsessie redt
Gepubliceerd op 18 April 2026
- Het probleem: een bezet terminal
- De drie onmisbare opdrachten
- Scenario 1 : De lange build
- Scenario 2 : De vergeten redacteur
- Scenario 3: Het proces opencode is onderbroken
- Scenario 4: Meerdere taken tegelijkertijd beheren
- Verschillen tussen fg, bg, Ctrl+Z, Ctrl+C en &
- De volledige levenscyclus van een job
- Geavanceerde tips
- Valkuilen en veelvoorkomende fouten
- Overzichtsdiagram
- Waarom fg onderschat is
- Technisch geheugen: de signalen
- Conclusie
- Referenties
U lanceert`./gradlew build`, de terminal is geblokkeerd gedurende 3 minuten, en je moet iets anders doen terwijl je wacht ? Je hoeft geen nieuw tabblad te openen.`Ctrl+Z`schorst het proces,`fg`Het brengt het precies terug waar je het liet. Dit is het meest onderschatte Unix-tip in de dagelijkse routine van een ontwikkelaar.
Het probleem: een bezet terminal
Elke ontwikkelaar heeft deze situatie meegemaakt:
$ ./gradlew build
> Building...
De terminal is geblokkeerd. Je kunt geen commando’s meer typen. En u moet een bestand bekijken, een starten.git status, of een bezette poort controleren.
@startuml skinparam backgroundColor #FEFEFE actor Développeur as dev participant "Terminal" as term participant "Gradle\n( proces )" as gradle dev -> term : ./gradlew bouwen term -> gradle : Start de build gradle -> term : Vul de terminal\n(zonder prompt) dev -> term : wil een opdracht typen term --> dev : Geblokkeerde terminal note right of dev De ontwikkelaar is verlamd gedurende de volledige duur van de build end note @enduml
De meeste ontwikkelaars openen een nieuwe terminal. Maar er bestaat een elegantere, snellere en natieve oplossing: het beheer van taken.
De drie onmisbare opdrachten
Ctrl+Z — Tijdelijk stoppen (SIGTSTP)
`Ctrl+Z`verzend het signaal`SIGTSTP`naar het proces in de voorgrond. Het proces isopgehangen— niet gedood — en het terminal krijgt zijn prompt terug.
$ ./gradlew build
> Building...
^Z
[1]+ Stoppé ./gradlew build
$
Het getal tussen haakjes`[1]`is dejobnummer. Le `+`geeft de standaardtaak aan.
fg — Voortzetten op de voorgrond
`fg`brengt de gepauzeerde taak naar de voorgrond. Het proces hervat precies waar het was gebleven.
$ fg
./gradlew build
> Building... (reprend où il en était)
Met een specifiek jobnummer:
$ fg %2
bg — Hervatten op de achtergrond
`bg`herneem het proces zonder de terminal te blokkeren. Ideaal voor lange builds.
$ bg
[1]+ ./gradlew build &
$
Le `&`Aan het einde betekent dat het proces op de achtergrond draait.
jobs — Toon de taken van de shell
$ jobs
[1]- Stoppé vim README.md
[2]+ En cours ./gradlew build
@startuml skinparam backgroundColor #FEFEFE skinparam defaultFontName sans-serif state "Voorgrond\n(geblokkeerde terminal)" as fg #LightCoral state "Opgeschort\n(Ctrl+Z)" as stopped #LightYellow state "Achtergrond\n(achtergrond)" as bg_ #LightGreen [*] --> fg : Lance une commande fg --> stopped : Ctrl+Z\n(SIGTSTP) stopped --> fg : fg\nreprend op de voorgrond stopped --> bg_ : bg\ngaat verder op de achtergrond bg_ --> fg : fg\nbrengt naar de voorgrond fg --> [*] : Bestelling voltooid note right of stopped Het proces is bevroren maar _pas_ gedood end note note left of bg_ Het terminal is vrij de bouw doorgaat end note @enduml
Scenario 1 : De lange build
Dit is het meest voorkomende gebruiksgeval. Je start een Gradle-build, de terminal is geblokkeerd, en je moet iets anders doen.
@startuml skinparam backgroundColor #FEFEFE autonumber actor Développeur as dev participant "Terminal" as term participant "Gradle" as gradle database "Status\nproces" as state dev -> term : ./gradlew bouw term -> gradle : Start de build gradle -> state : Bezig (FG) dev -> term : Ctrl+Z term -> state : Geschorst (job [1]) term --> dev : Vrije prompt $ dev -> term : git status term --> dev : werkboom schoon dev -> term : fg term -> state : Hervat (FG) gradle --> dev : Bouw geslaagd ✓ note right of state Het proces Gradle is nooit gedood Het hervat precies waar het was gebleven end note @enduml
Stap voor stap
$ ./gradlew build
> Building...
^Z
[1]+ Stoppé ./gradlew build
$ git status
On branch main
nothing to commit, working tree clean
$ fg
./gradlew build
> Building...
BUILD SUCCESSFUL in 2m 34s
Scenario 2 : De vergeten redacteur
U bent in`vim`, u moet een ander bestand raadplegen zonder te vertrekken`vim`.
# Dans vim, en mode commande :
:shell
$ cat /path/to/other/file.txt
$ exit
# Retour dans vim
Maar het is zwaar. Met`Ctrl+Z`, het is onmiddellijk :
# Dans vim, n'importe quel moment :
Ctrl+Z
[1]+ Stoppé vim README.md
$ cat /path/to/other/file.txt
# ... lire le fichier ...
$ fg
vim README.md
# Retour exact là où on en était, curseur en place
@startuml skinparam backgroundColor #FEFEFE autonumber actor Développeur as dev participant "vim" as vim participant "Shell" as shell dev -> vim : bewerking in uitvoering dev -> vim : Ctrl+Z vim -> shell : Geschorst (taak [1]) dev -> shell : kat autre_fichier.txt shell --> dev : inhoud van het bestand dev -> shell : fg shell -> vim : Continueer (job [1]) vim --> dev : Cursor precies\nop de dezelfde positie note right of vim vim herstelt intvolledig de staat : positie van de cursor, buffer, ongedaan maken stapel end note @enduml
vim`herstelt volmaakt zijn staat na een`fg: cursorpositie, bufferinhoud, geschiedenis van ongedaan maken — alles wordt bewaard.
Scenario 3: Het proces opencode is onderbroken
Dit is het scenario dat dit artikel heeft gemotiveerd. U gebruikt`opencode`(CLI-hulpmiddel om een LLM op je code te besturen), en het proces is opgeschort.
@startuml skinparam backgroundColor #FEFEFE skinparam defaultFontName sans-serif skinparam componentStyle rectangle actor Développeur as dev participant "opencode\n(LLM agent)" as agent participant "Shell" as shell participant "git" as git dev -> agent : Actieve sessie agent -> agent : Tour de parole\ndu LLM en cours note over agent Ctrl+Z verschijnt midden in een code-analyse end note dev -> agent : Ctrl+Z agent -> shell : Suspendu (job [1]) dev -> git : git diff git --> dev : zichtbare veranderingen dev -> shell : fg shell -> agent : Hervat (job [1]) agent --> dev : Reprend exactement\nl'analyse en cours note right of agent Geen verlies van context LLM hervat zijn antwoord Exact daar waar het was gebleven end note @enduml
$ opencode
# ... session en cours, LLM analyse le code ...
^Z
[1]+ Stoppé opencode
$ git diff HEAD~1
# vérifier les changements récents
$ fg
opencode
# Le LLM reprend là où il s'était arrêté
Het voordeel is dubbel:
-
Geen contextverlies: de LLM-sessie, de gespreksgeschiedenis, de geopende bestanden — alles wordt behouden
-
Geen herstart: geen noodzaak om opencode opnieuw te starten, de context opnieuw te laden, het probleem aan de LLM uit te leggen
Scenario 4: Meerdere taken tegelijkertijd beheren
fg et `bg`accepteren een jobnummer om een specifiek proces te targeten wanneer meerdere zijn opgeschort
$ vim config.yml
^Z
[1]+ Stoppé vim config.yml
$ ./gradlew test
^Z
[2]+ Stoppé ./gradlew test
$ htop
^Z
[3]+ Stoppé htop
$ jobs
[1] Stoppé vim config.yml
[2]- Stoppé ./gradlew test
[3]+ Stoppé htop
$ fg %2
./gradlew test
# Le build reprend en avant-plan
$ bg %3
[3] htop &
# htop reprend en arrière-plan
@startuml
skinparam backgroundColor #FEFEFE
rectangle "Gestopte taken" as jobs #LightYellow {
card "[1] vim config.yml" as j1
card "[2] ./gradlew test" as j2
card "[3] htop" as j3
}
rectangle "Acties" as actions #LightGreen {
card "fg %2\n→ voorgrond" as a1
card "bg %3\n→ achtergrond" as a2
card "fg %1\n→ voorgrond" as a3
}
jobs --> actions : fg/bg + jobnummer
actions --> j2 : Hervat
actions --> j3 : Op de achtergrond
note bottom of jobs
Elke taak behoudt
zijn exacte staat
tussen de suspend/resume
end note
@enduml
Overzicht van de jobopdrachten
| Bestelling | effect | Voorbeeld |
|---|---|---|
|
Suspendeer het proces op de voorgrond |
Verzending van`SIGTSTP` |
|
Hervat de standaardtaak in de voorgrond |
|
|
Hervat de job n op de voorgrond |
|
|
Neem de standaardtaak op de achtergrond |
|
|
Hervat job n op de achtergrond |
|
|
Lijst de taken van de huidige shell |
|
|
Voltooi de taak n |
|
Verschillen tussen fg, bg, Ctrl+Z, Ctrl+C en &
Veel ontwikkelaars verwarren deze mechanismen. Dit is een cruciale verduidelijking.
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
rectangle "**Ctrl+Z**\nSIGTSTP" as ctrlz #LightYellow {
card "Schors het proces\n(hij staat nog steeds in het geheugen)\nGebruikbaar met fg/bg" as c1
}
rectangle "**Ctrl+C**\nSIGINT" as ctrlc #LightCoral {
card "Dood het proces\n(geen terugkeer mogelijk)\nVerlies van de volledige staat" as c2
}
rectangle "**& **\n(arrière-plan)" as ampersand #LightGreen {
card "Start meteen\nop de achtergrond\nTerminal meteen vrij" as c3
}
rectangle "**fg**\n(foreground)" as fg_ #SkyBlue {
card "Brengt een taak\ngepauzeerd op de voorgrond\nTerminal bezet" as c4
}
rectangle "**bg**\n(background)" as bg_ #Lavender {
card "Hervat een taak\ngepauzeerd op de achtergrond\nTerminal vrij" as c5
}
ctrlz -right-> fg_ : fg om te hervatten
ctrlz -right-> bg_ : bg om verder te gaan\nop de achtergrond
ctrlc -right-> c2 : Geen retour\nproces gedood
note bottom of ctrlz
Het proces is **vastgelopen**
maar **niet gedood**
end note
note bottom of ampersand
Alternatief: start ./gradlew build & direct op de achtergrond
end note
@enduml
| Sneltoets/Commando | signaal | Effect | Teruggaan? |
|---|---|---|---|
|
|
Schors het proces (bevroren, in het geheugen) |
Ja:`fg` ou |
|
|
Stop het proces (afgerond) |
Nee : vernietigd proces |
|
|
Stop het proces + core dump |
Nee: proces vernietigd |
|
— |
Start direct in de achtergrond |
`fg`om het naar voren te brengen |
Ctrl+Z`isniet-destructief. Het proces is vastgehouden zoals het is, in het geheugen. U kunt direct verdergaan met`fg. Het is een pause, geen stop.
|
De volledige levenscyclus van een job
@startuml skinparam backgroundColor #FEFEFE skinparam defaultFontName sans-serif title Volledige levenscyclus van een shell-job state "Niet bestaand" as none state "Voorgrond\n(neemt de terminal in beslag)" as foreground #LightCoral state "Gepauzeerd\n(Ctrl+Z / SIGTSTP)" as suspended #LightYellow state "Achtergrond\n(bg of &)" as background #LightGreen state "Afgerond\n(afsluitcode 0)" as done #LightGray [*] --> none none --> foreground : typ een opdracht foreground --> suspended : Ctrl+Z foreground --> done : normaal einde suspended --> foreground : fg suspended --> background : (Empty) suspended --> done : kill %n background --> foreground : fg background --> done : einde normaal\n(op de achtergrond) background --> suspended : Ctrl+Z\n(als fg dan Ctrl+Z) done --> [*] note right of suspended De opgeschorte staat is het draaipunt van het hele jobsysteem end note @enduml
Geavanceerde tips
fg met de meest recente baan en de afwisseling
fg`zonder argument brengt de gemarkeerde taak+(het meest recente). Maar je kunt ook gebruiken%-`om op de vorige taak te richten :
$ vim file1.txt
^Z
[1]+ Stoppé vim file1.txt
$ vim file2.txt
^Z
[2]+ Stoppé vim file2.txt
$ fg # ramène job [2] (le plus récent)
$ fg %- # ramène job [1] (le précédent)
een job netjes afmaken
$ jobs
[1]- Stoppé vim config.yml
[2]+ Stoppé ./gradlew test
$ kill %2 # envoie SIGTERM au job 2
$ kill -9 %1 # envoie SIGKILL au job 1 (force)
désown : loskoppelen van de shell
disown`Verwijder een job uit de job-tabel van de shell. Het proces blijft draaien maar kan niet meer worden teruggehaald met`fg:
$ ./gradlew build &
[1] 12345
$ disown %1
# Le processus continue mais n'est plus lié au shell
# Ça survives à la fermeture du terminal
Combineer met nohup voor langdurige processen
Zodat een proces blijft bestaan na het sluiten van de terminal:
$ nohup ./gradlew build &
[1] 12345
$ disown %1
# Le build continue même si vous fermez le terminal
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
rectangle "fg" as fg_ #SkyBlue {
card "job\nhet meest recente (+)" as f1
card "Richt een job\nspecifiek : fg %n" as f2
card "Wissel : fg %-" as f3
}
rectangle "kill %n" as kill_ #LightCoral {
card "SIGTERM (net)" as k1
card "SIGKILL -9 (dwang)" as k2
}
rectangle "disown" as disown #Lavender {
card "Maakt los van de shell\n(blijft bestaan na afsluiting)" as d1
}
rectangle "nohup + &" as nohup_ #LightGreen {
card "Proces ongevoelige\ntegen SIGHUP\n(sluiting\nde terminal)" as n1
}
fg_ -right-> kill_ : als moet\nvoltooien
kill_ -right-> disown_ : of losmaken
nohup_ -left-> fg_ : fg haalt en\navant-plan
note bottom of disown_
Na `disown` kan `fg` de taak niet meer naar voren brengen.
end note
@enduml
Valkuilen en veelvoorkomende fouten
Ctrl+Z en Ctrl+C verwarren
Dit is de meest voorkomende fout.`Ctrl+C`doodt het proces.`Ctrl+Z`Het opschort. Als je drukt op`Ctrl+C`Reflexmatig is de hele staat verloren — geopende bestanden, LLM-sessies, lopende builds.
@startuml
skinparam backgroundColor #FEFEFE
rectangle "Ctrl+Z (SIGTSTP)" as z #LightGreen {
card "Proces bevroren in het geheugen" as z1
card "fg brengt het intact terug" as z2
card "Geen verlies" as z3
}
rectangle "Ctrl+C (SIGINT)" as c #LightCoral {
card "Proces beëindigd" as c1
card "Toestand definitief verloren" as c2
card "Geen terugkeer mogelijk" as c3
}
note bottom of z
Pauze → Hervatten
end note
note bottom of c
Kill → Verloren
end note
@enduml
De jobs zijn lokaal voor de shell
jobs, fg et `bg`Ze werken alleen in de shell die de processen heeft gestart. Als je een nieuw terminal opent, zie je de jobs van de ander niet.
# Terminal 1
$ vim file.txt
^Z
[1]+ Stoppé vim file.txt
# Terminal 2 (nouveau)
$ jobs
# (rien — les jobs sont locaux au shell)
Om de systeemwijdere processen te zien, gebruik`ps` ou htop:
ps aux | grep vim
Jobs overleven de afsluiting van de shell niet
Als je de terminal sluit, worden alle tijdelijk gestopte of op de achtergrond draaiende taken vernietigd. Om een proces in leven te houden, gebruik je`nohup`+disown(Empty output)
$ nohup ./gradlew build &
[1] 12345
$ disown %1
# Fermer le terminal → le build continue
De interactieve processen en fg
Sommige interactieve programma’s doen het niet goed na een`Ctrl+Z`+fg. Het is zeldzaam maar het gebeurt:
-
Sommige programma’s die signalen gebruiken (vim, htop, less hanteren goed`Ctrl+Z`)
-
Lange netwerkprogramma’s (ssh, opencode) herstellen zich meestal correct
-
Programma’s die de terminal (ncurses) wijzigen, kunnen soms de terminal in een onverwachte staat achterlaten.
In geval van een beschadigde terminal na een`fg`, typ :
reset
Overzichtsdiagram
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444
title "fg — De tip die alles verandert"
rectangle "**Probleem**" as problem #LightCoral {
card "Terminal geblokkeerd\ndoor een proces" as p1
card "Nieuwe terminal openen = omslachtige oplossing" as p2
}
rectangle "**Oplossing**" as solution #LightGreen {
card "Ctrl+Z → pauzeren" as s1
card "fg → hervatten" as s2
card "bg → op de achtergrond" as s3
card "jobs → gepauzeerde processen bekijken" as s4
}
rectangle "**Voordelen**" as advantages #SkyBlue {
card "Geen verlies van context" as a1
card "Geen herstart nodig" as a2
card "Eén terminal is voldoende" as a3
}
problem -right-> solution : "De native Unix-oplossing\nvan 40 jaar geleden"
solution -right-> advantages : "Resultaat"
note bottom of advantages
fg is geen obscure tip
Het is een fundamenteel mechanisme
van de Unix shell
end note
@enduml
Waarom fg onderschat is
De meeste moderne ontwikkelaars ontdekken`fg`laat, of nooit. Enkele redenen :
-
IDE’s verbergen de terminal: VS Code biedt een ingebouwde terminal, maar meerdere tabbladen maken`fg`schijnbaar minder nodig
-
Grafische tools vervangen de CLI: bestandsbeheerders, systeemmonitors, grafische Git-clients
-
tmux en screen: deze multiplexers lossen hetzelfde probleem op een andere manier op, maar`fg`is eenvoudiger en altijd beschikbaar
Toch,`fg`is :
-
universeel: aanwezig in alle POSIX-shells (bash, zsh, fish…)
-
Zonder afhankelijkheid: niet nodig om tmux of screen te installeren
-
momentopname: twee slagen (
Ctrl+Z`dan`fg) voor opschorten en hervatten -
Contextbehoud: de staat van het proces is intact
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
rectangle "Waarom is `fg`\nonderschat?" as why #LightCoral {
card "IDE met terminal\nmet meerdere tabbladen" as w1
card "Grafische tools\ndie de CLI vervangen" as w2
card "tmux/screen\nlossen het probleem\nop een andere manier op" as w3
}
rectangle "Waarom is `fg`\nsuperieur?" as superior #LightGreen {
card "Universeel: bash,\nzsh, fish, sh..." as s1
card "Zonder afhankelijkheid:\ngeen installatie" as s2
card "Twee toetsaanslagen:\nCtrl+Z en dan `fg`" as s3
card "Context behouden:\nnul verlies van status" as s4
}
why -right-> superior : "Eenvoud\nwint altijd"
@enduml
Technisch geheugen: de signalen
Voor de nieuwsgierigen, hier zijn de betrokken signalen :
| signaal | Naam | Verzonden door | Effect |
|---|---|---|---|
2 |
|
|
Onderbreking — beëindigt het proces |
3 |
|
|
Verlaat met core dump |
18 |
|
|
Interactieve stop — schort het proces op |
19 |
|
|
Doorgaan — hervat een onderbroken proces |
15 |
|
|
zuivere beëindiging |
9 |
|
|
Geforceerde beëindiging (kan niet worden onderschept) |
De volledige stroom :
@startuml skinparam backgroundColor #FEFEFE skinparam defaultFontName sans-serif participant "Gebruiker" as user participant "Shell" as shell participant "Proces" as proc user -> shell : Ctrl+Z shell -> proc : SIGTSTP (signal 18) note right of proc : Bevroren proces\nin het geheugen user -> shell : fg shell -> proc : SIGCONT (signal 19) note right of proc : Proces wordt hervat\nop de voorgrond user -> shell : bg shell -> proc : SIGCONT (signal 19) note right of proc : Proces wordt hervat\nop de achtergrond user -> shell : Ctrl+C shell -> proc : SIGINT (signal 2) note right of proc : Proces voltooid\ntoestand verloren @enduml
Conclusie
`fg`Het is geen duistere tip — het is een fundamenteel mechanisme van de Unix-shell, beschikbaar sinds de jaren 70. Het feit dat de meeste ontwikkelaars het negeren, is een symptoom van onze tijd: IDE’s en multiplexers hebben ons van de basis verwijderd.
Maar wanneer u op een SSH-server bent, in een minimale terminal, of u gewoon aan het gebruiken bent`opencode`en dat de LLM volop aan het werk is —Ctrl+Z, fg, bg, `jobs`zijn je beste vrienden.
_ Ctrl+Z schorst, fg hervat. Dit is de pauze-speel van de terminal. _
Referenties
-
man bash— sectie JOB CONTROL -
man signal— lijst van POSIX-signalen