زمان خواندن : 10 minutes

شما راه‌اندازی می‌کنید`./gradlew build`, ترمینال برای 3 دقیقه مسدود می‌شود، و شما نیاز دارید کاری دیگر انجام دهید در حال انتظار؟ لازم نیست یک تب جدید باز کنید.`Ctrl+Z`فرآیند را متوقف می‌کند،`fg`او را دقیقاً جایی که شما آن را ترک کرده بودید، باز می‌گرداند. این نکته Unix، کم‌تقویم‌ترین نکته در زندگی روزانه یک توسعه‌دهنده است.

تیک

[]

مشکل: یک ترمینال مشغول

هر توسعه‌دهنده این وضعیت را تجربه کرده است :

$ ./gradlew build
> Building...

ترمینال مسدود شده است. دیگر نمی‌توانید هیچ دستوری وارد کنید. و شما به بررسی یک فایل و اجرای یک …​git status، یا بررسی یک پورت اشغال.

@startuml
skinparam backgroundColor #FEFEFE

actor Développeur as dev
participant "ترمینال" as term
participant "Gradle\n( فرآیند )" as gradle

dev -> term : ./gradlew build
term -> gradle : Lance le build
gradle -> term : Occupe le terminal\n(pas de prompt)
dev -> term : veut taper une commande
term --> dev : ❌ Terminal bloqué

note right of dev
  Le développeur est
  paralysé pendant
  toute la durée du build
end note
@enduml

بسیاری از توسعه‌دهندگان یک ترمینال جدید باز می‌کنند. اما راه‌حلی بهتر، زیباتر، سریع‌تر و بومی وجود دارد: مدیریت کارها

سه دستور ضروری

Ctrl+Z — تعلیق (SIGTSTP)

`Ctrl+Z`سیگنال را می‌فرستد`SIGTSTP`به فرآیند پیش‌روی. فرآیند استمعلق— نکشته نشده — و ترمینال پرامپت خود را باز می‌یابد.

$ ./gradlew build
> Building...
^Z
[1]+  Stoppé    ./gradlew build
$

عدد بین کروشه`[1]`استشماره شغل. Le `+`شغل پیش‌فرض را نشان می‌دهد.

fg — آوردن به پیش‌زمینه

`fg`شغل معلق را به پیش‌زمینه می‌آورد. فرآیند دقیقاً جایی که متوقف شده بود، ادامه می‌دهد.

$ fg
./gradlew build
> Building... (reprend où il en était)

با یک شماره کار مخصوص :

$ fg %2

bg — ادامه در پس‌زمینه

`bg`فرآیند را بدون مسدود کردن ترمینال ادامه می‌دهد. مناسب برای ساخت‌های طولانی.

$ bg
[1]+ ./gradlew build &
$

Le `&`در پایان، این به این معناست که پردازش در پس‌زمینه در حال اجرا است.

jobs — لیست کارهای شل

$ jobs
[1]-  Stoppé    vim README.md
[2]+  En cours   ./gradlew build
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

state "پیش‌زمینه
(ترمینال مسدود)" as fg #LightCoral
state "متوقف
(Ctrl+Z)" as stopped #LightYellow
state "پس‌زمینه
(bg)" as bg_ #LightGreen

[*] --> fg : Lance une commande
fg --> stopped : Ctrl+Z\n(SIGTSTP)
stopped --> fg : fg\nreprend en avant-plan
stopped --> bg_ : bg\nreprend en arrière-plan
bg_ --> fg : fg\nramène en avant-plan
fg --> [*] : Commande terminée

note right of stopped
  Le processus est figé
  mais _pas_ tué
end note

note left of bg_
  Le terminal est libre
  le build continue
end note
@enduml

سناریو ۱ : ساخت طولانی

این مورد استفاده رایج‌ترین است. شما یک ساخت Gradle اجرا می‌کنید، ترمینال مسدود می‌شود و شما نیاز دارید کاری دیگر انجام دهید.

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor Développeur as dev
participant "ترمینال" as term
participant "گریڈل" as gradle
database "وضعیت
فرآیند" as state

dev -> term : ./gradlew build
term -> gradle : Lance le build
gradle -> state : En cours (FG)
dev -> term : Ctrl+Z
term -> state : Suspendu (job [1])
term --> dev : Prompt libre $

dev -> term : git status
term --> dev : working tree clean

dev -> term : fg
term -> state : Reprend (FG)
gradle --> dev : Build réussi ✓

note right of state
  Le processus Gradle
  n'a jamais été tué
  Il reprend exactement
  où il s'était arrêté
end note
@enduml

قدم به قدم

$ ./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

سناریو ۲ : ویراستار فراموش شده

شما در`vim`, شما نیاز دارید تا یک فایل دیگر را مشاهده sans خروج کنید.vim.

# Dans vim, en mode commande :
:shell
$ cat /path/to/other/file.txt
$ exit
# Retour dans vim

اما این سنگین است. با`Ctrl+Z`، این بلافاصله است :

# 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 "حیویت" as vim
participant "شل" as shell

dev -> vim : édition en cours
dev -> vim : Ctrl+Z
vim -> shell : Suspendu (job [1])
dev -> shell : cat autre_fichier.txt
shell --> dev : contenu du fichier
dev -> shell : fg
shell -> vim : Reprend (job [1])
vim --> dev : Curseur exactement\nà la même position

note right of vim
  vim restaure
  intégralement l'état :
  position du curseur,
  buffer, undo stack
end note
@enduml
vim`وضعیتش را به‌صورت کامل پس از یک بازگردانده می‌شود`fg: موقعیت کورسور، محتوی بافر، تاریخ لغو — همه چیز حفظ شده است.

سيناريو 3 : پردازش opencode معلق

این سناریویی است که این مقاله را ترغیب کرده است. شما استفاده می‌کنید`opencode`(ابزار CLI برای کنترل یک LLM بر روی کد شما)، و عملية معلق است

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam componentStyle rectangle

actor Développeur as dev
participant "opencode
(نماینده LLM)" as agent
participant "قشر" as shell
participant "گیت" as git

dev -> agent : Session active
agent -> agent : Tour de parole\ndu LLM en cours

note over agent
  Ctrl+Z arrive
  en plein milieu
  d'une analyse de code
end note

dev -> agent : Ctrl+Z
agent -> shell : Suspendu (job [1])
dev -> git : git diff
git --> dev : changements visibles
dev -> shell : fg
shell -> agent : Reprend (job [1])
agent --> dev : Reprend exactement\nl'analyse en cours

note right of agent
  Aucune perte de contexte
  Le LLM reprend sa réponse
  exactement où il en était
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é

مزیت دوگانه است :

  1. هیچ از دست رفتن زمینه: جلسه LLM، تاریخچه گفتگو، فایل‌های باز شده — همه حفظ شده است

  2. بدون راه‌اندازی مجدد: نیازی نیست opencode را دوباره راه‌اندازی کنیم، زمینه را بارگذاری مجدد کنیم، یا مشکل را به مدل زبانی بزرگ (LLM) توضیح دهیم.

سناریو ۴ : مدیریت چندین کار به‌صورت همزمان

fg et `bg`قبول می‌کنند یک شماره کار برای هدف‌گیری یک فرآیند خاص هنگامی که چند مورد معلق هستند.

$ 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 "شغل‌های معلق" as jobs #LightYellow {
  card "[1] vim config.yml" as j1
  card "[2] ./gradlew test" as j2
  card "[3] htop" as j3
}

rectangle "اقدامات" as actions #LightGreen {
  card "fg %2
→ پیش‌زمینه" as a1
  card "bg %3\n→ background" as a2
  card "fg %1\n→ پیش‌زمینه" as a3
}

jobs --> actions : fg/bg + numéro de job
actions --> j2 : Reprend
actions --> j3 : En arrière-plan

note bottom of jobs
  Chaque job conserve
  son état exact
  entre les suspend/resume
end note
@enduml

خلاصه از دستورات کار

دستور اثر مثال

Ctrl+Z

فرآیند پیش‌زمینه را تعلیق می‌دهد

ارسال`SIGTSTP`

fg

کار پیش‌فرض را درforeground بگیرید

fg ou fg %1

fg %n

کار n را در پیش‌زمین ادامه دهید

fg %2

bg

بازگشت به کار پیش‌فرض در پس‌زمینه

bg ou bg %1

bg %n

ادامه job n در پس‌زمینه

bg %2

jobs

وظائف شل جاری را فهرست کنید

jobs -l(به همراه PID)

kill %n

کار n را تمام کن

kill %2

تفاوت‌ها بین fg, bg, Ctrl+Z, Ctrl+C و &

چندین توسعه‌دهنده این مکانیزم‌ها را اشتباه می‌فهمند. این یک توضیح قطعی است.

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle "**Ctrl+Z**\nSIGTSTP" as ctrlz #LightYellow {
  card "فرآیند را متوقف کنید
(هنوز در حافظه است)
قابل استفاده با fg/bg" as c1
}

rectangle "**Ctrl+C**\nSIGINT" as ctrlc #LightCoral {
  card "فرآیند را بکش
(بازگشت ممکن نیست)
از دست دادن کل وضعیت" as c2
}

rectangle "**& **\n(پس‌زمینه)" as ampersand #LightGreen {
  card "مستقیماً اجرا کن
در پس‌زمینه
ترمینال آزاد فوراً" as c3
}

rectangle "**fg**
(پیش‌زمینه)" as fg_ #SkyBlue {
  card "یک کار را بیار
در حالت تعلقی در پیش‌زمینه
ترمینال مشغول" as c4
}

rectangle "**bg**
(پیش‌زمینه)" as bg_ #Lavender {
  card "ادامه دادن به یک کار معلق در پس‌زمینه
ترمینال آزاد" as c5
}

ctrlz -right-> fg_ : fg pour reprendre
ctrlz -right-> bg_ : bg pour continuer\nen arrière-plan
ctrlc -right-> c2 : Pas de retour\nprocessus tué

note bottom of ctrlz
  Le processus est **figé**
  mais **pas tué**
end note

note bottom of ampersand
  Alternative : lancer
  ./gradlew build &
  directement en arrière-plan
end note
@enduml
میانبر/دستور سیگنال اثر بازگشت به عقب؟

Ctrl+Z

SIGTSTP

فرآیند را متوقف کند (مجمد، در حافظه)

بله:`fg` ou bg

Ctrl+C

SIGINT

فرآیند را بکشید (پایان یافت)

نه: فرآیند تخریب شده

Ctrl+\

SIGQUIT

فرآیند را متوقف کنید + core dump

نه : فرایند تخریب شده

commande &

—

مستقیم در پس‌زمینه اجرا کن

`fg`برایBring it به پیش‌pland برگرداندن

Ctrl+Z`استغیرمخرب.فرآیند به همان صورتی که است، در حافظه، منجمد شده است. می‌توانید بلافاصله آن را ادامه دهید با`fg. این یک pause است، نه stop .

دورهٔ زندگی کامل یک کار

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

title دوره کامل زندگی یک کار شِل
state "غیر موجود" as none
state "پیش‌زمینه
(ترمینال را اشغال می‌کند)" as foreground #LightCoral
state "متوقف\n(Ctrl+Z / SIGTSTP)" as suspended #LightYellow
state "پس‌زمینه\n(bg یا &)" as background #LightGreen
state "تمام
(kod خروج 0)" as done #LightGray

[*] --> none
none --> foreground : tape une commande
foreground --> suspended : Ctrl+Z
foreground --> done : fin normale

suspended --> foreground : fg
suspended --> background : bg
suspended --> done : kill %n

background --> foreground : fg
background --> done : fin normale\n(en arrière-plan)
background --> suspended : Ctrl+Z\n(si fg puis Ctrl+Z)

done --> [*]

note right of suspended
  L'état suspendu est
  le pivot de tout le
  système de jobs
end note
@enduml

نکات پیشرفته

fg با کار جدیدترین و تناوب

fg`بدون آرگومان، کار علامت‌گذاری‌شده برمی‌گردد+(جدیدترین). اما شما نیز می‌توانید از آن استفاده کنید%-`برای هدف‌گیری به کار قبلی :

$ 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)

یک کار را به خوبی انجام دادن

$ 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)

disown : جدا کردن از شل

disown`یک کار از جدول کارهای شل حذف می‌شود. فرآیند همچنان در حال اجراست اما دیگر نمی‌تواند با بازگردانده شود.`fg(Note: a single space character)

$ ./gradlew build &
[1] 12345

$ disown %1
# Le processus continue mais n'est plus lié au shell
# Ça survives à la fermeture du terminal

ترکیب با nohup برای پردازش‌های طولانی

به منظور اینکه یک پروسه پس از بستن ترمینال زنده بماند :

$ 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 "کار را بیاور
جدیدترین (+)" as f1
  card "یک شغل\nخاص : fg %n" as f2
  card "متناوب : fg %-%" as f3
}

rectangle "کش %n" as kill_ #LightCoral {
  card "SIGTERM (پاک)" as k1
  card "SIGKILL -9 (به‌صورت اجباری)" as k2
}

rectangle "انکار" as disown #Lavender {
  card "از شل جدا شو
(در قطع اتصال زنده می‌ماند)" as d1
}

rectangle "nohup + &" as nohup_ #LightGreen {
  card "فرآیند ضد SIGHUP
(باقی می‌ماند هنگام بستن ترمینال)" as n1
}

fg_ -right-> kill_ : si faut\nterminer
kill_ -right-> disown_ : ou détacher
nohup_ -left-> fg_ : fg ramène en\navant-plan

note bottom of disown_
  Après disown, fg ne peut
  plus ramener le job
end note
@enduml

پنجه‌ها و اشتباهات رایج

اشتباه گرفتن Ctrl+Z و Ctrl+C

این اشتباه رایج‌ترین است.`Ctrl+C`فرآیند را می‌کشد.`Ctrl+Z`به متوقف می‌کند. اگر شما فشار دهید بر`Ctrl+C`به طور انعکاسی، تمام وضعیت از دست رفته است — فایل‌های باز، جلسات LLM، ساخت‌های در حال انجام.

@startuml
skinparam backgroundColor #FEFEFE

rectangle "Ctrl+Z (SIGTSTP)" as z #LightGreen {
  card "فرآیند منجمد در حافظه" as z1
  card "fg او را به‌سلامت برمی‌گرداند" as z2
  card "هیچ ضرر" as z3
}

rectangle "Ctrl+C (SIGINT)" as c #LightCoral {
  card "فرآیند به پایان رسید" as c1
  card "وضعیت به‌طور دائم گم شده" as c2
  card "بازگشت ممکن نیست" as c3
}

note bottom of z
  Pause → Reprendre
end note

note bottom of c
  Kill → Perdu
end note
@enduml

کارها محلی به شل هستند

jobs, fg et `bg`فقط در شل که процес‌ها را راه‌اندازی کرده‌اند، کار می‌کنند. اگر یک ترمینال جدید باز کنید، jobs دیگر را نخواهید دید.

# Terminal 1
$ vim file.txt
^Z
[1]+  Stoppé    vim file.txt

# Terminal 2 (nouveau)
$ jobs
# (rien — les jobs sont locaux au shell)

برای دیدن فرآیندهای system-wide، استفاده کنید`ps` ou htop:

ps aux | grep vim

کارها پس از بستن شِل، به بقا نمی‌مانند

اگر ترمینال را ببندید، تمام کارهای موقفه‌دار یا در پس‌زمینه حذف می‌شوند. برای بقاوندن یک فرآیند، استفاده کنید`nohup` + disown :

$ nohup ./gradlew build &
[1] 12345
$ disown %1
# Fermer le terminal → le build continue

فرآیندهای تعاملی و fg

برخی برنامه‌های تعاملی نه به خوبی عمل می‌کنند پس از یک`Ctrl+Z`+fg. نادر است اما این اتفاق می‌افتد :

  • برخی برنامه‌هایی که از سیگنال‌ها استفاده می‌کنند (vim, htop, less به خوبی مدیریت می‌شوند`Ctrl+Z`)

  • برنامه‌های شبکه‌ای طولانی (ssh, opencode) معمولاً به‌درستی بازیابی می‌شوند

  • برنامه‌هایی که ترمینال را (ncurses) تغییر می‌دهند گاهی اوقات ترمینال را در وضعیت نامنتظر باقی می‌گذارند

در صورت خراب شدن ترمینال پس از یک`fg`, تایپ کنید :

reset

نمودار خلاصه

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif
skinparam ArrowColor #444444

title "fg — نکتی که همه چیز را تغییر می‌دهد"

rectangle "**مشکل**" as problem #LightCoral {
  card "ترمینال مسدود\nتوسط یک پردازش" as p1
  card "باز کردن یک\nترمینال = راه حل سنگین" as p2
}

rectangle "حل" as solution #LightGreen {
  card "Ctrl+Z → تعلیق" as s1
  card "fg → ادامه می‌دهد" as s2
  card "bg → پس‌زمینه" as s3
  card "وظائف → معلق‌ها را ببینید" as s4
}

rectangle "مزایا" as advantages #SkyBlue {
  card "هیچ از دست رفتن زمینه" as a1
  card "بدون راه‌اندازی مجدد" as a2
  card "یک ترمینال کافی است" as a3
}

problem -right-> solution : "راه‌حل بومی
یونیکس به مدت ۴۰ سال"
solution -right-> advantages : "نتیجه"

note bottom of advantages
  fg n'est pas un tips obscur
  C'est un mécanisme fondamental
  du shell Unix
end note
@enduml

چرا fg کم‌ارزش‌گذاری شده است؟

اکثر توسعه دهندگان معاصر کشف می‌کنند`fg`دیر، شاید هرگز. چند دلیل :

  1. IDEها ترمینال را پنهان می‌کنند: VS Code یک ترمینال داخلی ارائه می‌دهد، اما تب‌های多let`fg`به‌نظر کمتر لازم

  2. ابزارهای گرافیکی CLI را جایگزین می‌کنند: مدیران فایل، مانیتورهای سیستم، کلاینت‌های گرافیکی Git

  3. tmux و screen: این مولติ‌پلکسورها همان مشکل را به شکل متفاوتی حل می‌کنند، اما`fg`ساده‌تر و همیشه در دسترس است

اما,`fg`است :

  1. عالمی: موجود در همه شل‌های POSIX (bash, zsh, fish…​)

  2. بدون وابستگی: لازم نیست tmux یا screen را نصب کنید

  3. لحظه‌ای: دو ضربات (Ctrl+Z`سپس`fg) برای تعلیق و ادامه

  4. نگهدارنده زمینهوضعیت فرآیند سالم است

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

rectangle "چرا fg است
تقلیل‌قدر شده؟" as why #LightCoral {
  card "IDE با ترمینال\nچند برگه" as w1
  card "ابزارهای گرافیکی
که CLI را جایگزین می‌کنند" as w2
  card "tmux/screen
مشکل را حل می‌کنند
به طور دیگر" as w3
}

rectangle "چرا fg بهتر\nاست؟" as superior #LightGreen {
  card "عالمی : bash,
zsh, fish, sh..." as s1
  card "بدون وابستگی:
بدون نصب" as s2
  card "دو ضربه:\nCtrl+Z سپس fg" as s3
  card "محیط حفظ‌شده :\nبدون فقدان وضعیت" as s4
}

why -right-> superior : "سادهگی
همیشه برنده است"
@enduml

ذاكرة فنی: ال سیگنال‌ها

برای کسانی که سوال دارند، این است:

سیگنال نام ارسال شده توسط اثر

2

SIGINT

Ctrl+C

انقطاع — فرآیند را پایان می‌دهد

3

SIGQUIT

Ctrl+\

خروج با core dump

18

SIGTSTP

Ctrl+Z

Stop تعاملی — فرآیند را متوقف کنید

19

SIGCONT

fg/bg

ادامه — فرآیند معلق را ادامه می‌دهد

15

SIGTERM

kill %n

پایان تمیز

9

SIGKILL

kill -9 %n

پایان‌گیری اجباری (امکان interceptions نیست)

جريان کامل :

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

participant "کاربر" as user
participant "شل" as shell
participant "فرآیند" as proc

user -> shell : Ctrl+Z
shell -> proc : SIGTSTP (signal 18)
note right of proc : Processus figé\nen mémoire

user -> shell : fg
shell -> proc : SIGCONT (signal 19)
note right of proc : Processus reprend\nen avant-plan

user -> shell : bg
shell -> proc : SIGCONT (signal 19)
note right of proc : Processus reprend\nen arrière-plan

user -> shell : Ctrl+C
shell -> proc : SIGINT (signal 2)
note right of proc : Processus terminé\nétat perdu
@enduml

نتیجه

`fg`این یک نکته مبهم نیست — این مکانیزم اساسی شل یونیکس است که از دههٔ ۱۹۷۰ در دسترس بوده است. این که اکثر توسعه‌دهندگان آن را نادیده می‌گیرند، نشانه‌ای از زمان ماست: IDEها و مولتی‌پلکسورها ما را از اصول پایه دور کرده‌اند.

اما وقتی که روی یک سرور SSH هستید، در یک ترمینال کمینه، یا صرفاً در حال استفاده`opencode`و که LLM به شدت در حال کار است—Ctrl+Z, fg, bg, `jobs`بهترین دوستان شما هستند

__ Ctrl+Z متوقف می‌شود, fg ادامه می‌دهد. این pause-play ترمینال است. </think>

مقالات مرتبط