وقت القراءة: 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 بناء
term -> gradle : نفّذ البناء
gradle -> term : يشغل الطرفية\n(بدون موجه)
dev -> term : يريد كتابة أمر
term --> dev : الطرفية محجوبة

note right of dev
المطور مشلول طوال مدة البناء
end note
@enduml

معظم المطورين يفتحون وحدة طرفية جديدة. ولكن هناك حلًا أكثر أناقة، وأسرع، وناتي: إدارة المهام.

الثلاثة الأوامر الضرورية

Ctrl+Z — إيقاف (SIGTSTP)

`Ctrl+Z`يُرسل الإشارة`SIGTSTP`إلى العملية في المقدمة. العملية هيمعلق— غير مقتول — و استعاد terminal موجهه.

$ ./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`يستأنف العملية بدون حظرterminal. مثالي للبناءات الطويلة.

$ 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 "الواجهة الأمامية\n(الطرفية المحظورة)" as fg #LightCoral
state "معلق\n(Ctrl+Z)" as stopped #LightYellow
state "خلفية\n(bg)" as bg_ #LightGreen

[*] --> fg : Lance une commande
fg --> stopped : Ctrl+Z\n(SIGTSTP)
stopped --> fg : fg\nيعيد إلى المقدمة
stopped --> bg_ : bg\nreprend en arrière-plan
bg_ --> fg : fg\nيجلب إلى المقدمة
fg --> [*] : تم إكمال الأمر

note right of stopped
العملية متجمدة لكنها ليست مقتولة
end note

note left of bg_
الطرفية متاحة
البناء مستمر
end note
@enduml

سيناريو 1 : البناء الطويل

هذا هو أكثر حالات الاستخدام شيوعًا. أنت تشغل عملية بناء Gradle، والطرفيّة متوقفة، وتحتاج إلى فعل شيء آخر.

@startuml
skinparam backgroundColor #FEFEFE
autonumber

actor Développeur as dev
participant "الطرافية" as term
participant "Gradle" as gradle
database "الحالة\nالعملية" as state

dev -> term : ./gradlew بناء
term -> gradle : تشغيل البِيلد
gradle -> state : قيد التنفيذ (FG)
dev -> term : Ctrl+Z
term -> state : معلق (وظيفة [1])
term --> dev : طلبٌ مفتوح $

dev -> term : حالة git
term --> dev : شجرة العمل نظيفة

dev -> term : فج
term -> state : استأنف (FG)
gradle --> dev : تم البناء بنجاح ✓

note right of state
العملية Gradle
لم يُقتل قط
تستأنف exactamente
حيث توقفت
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

سيناريو 2 : المحرر منسي

أنت في`vim`, تحتاج إلى مراجعة ملف آخر دون الخروج`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 "vim" as vim
participant "قشرة" as shell

dev -> vim : جارٍ التحرير
dev -> vim : Ctrl+ز
vim -> shell : معلق (عمل [1])
dev -> shell : قطة autre_fichier.txt
shell --> dev : محتوى الملف
dev -> shell : fg
shell -> vim : استأنف (job [1])
vim --> dev : المؤشر بدقة\nفي نفس الموقع

note right of vim
vim يستعيد
intالحالة بالكامل :
موضع المؤشر,
المؤقت, مكدس التراجع
end note
@enduml

( empty )`vim`يستعيد حالته تمامًا بعد`fg`موقع المؤشر، محتوى المخزن المؤقت، سجل التراجع — كل شيء محفوظ.

السيناريو 3: عملية opencode معلقة

هذا هو السيناريو الذي حفز هذا المقال. أنت تستخدم`opencode`(أداة سطر أوامر لتشغيل نموذج لغوي كبير على كودك)، وال عملية معلقة.

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

actor Développeur as dev
participant "opencode\n(LLM وكيل)" as agent
participant "غلاف" as shell
participant "git" as git

dev -> agent : جلسة نشطة
agent -> agent : Tour de parole\ndu LLM en cours

note over agent
Ctrl+Z يأتي في منتصف تحليل الكود
end note

dev -> agent : Ctrl+Z
agent -> shell : Suspendu (job [1])
dev -> git : الفرق git
git --> dev : التغيّرات المرئية
dev -> shell : fg
shell -> agent : استأنف (وظيفة [1])
agent --> dev : Reprend exactement\nl'analyse en cours

note right of agent
لا فقدان للسياق
LLM يستأنف إجابته
بالضبط حيث كان
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

السيناريو 4: إدارة وظائف متعددة في وقت واحد

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 اختبار" as j2
  card "[3] htop" as j3
}

rectangle "الإجراءات" as actions #LightGreen {
  card "fg %2\n→ في المقدمة" as a1
  card "bg %3\n→ في الخلفية" as a2
  card "fg %1\n→ في المقدمة" as a3
}

jobs --> actions : fg/bg + رقم المهمة
actions --> j2 : استأنف
actions --> j3 : في الخلفية

note bottom of jobs
كل job يحتفظ
حالته الدقيقة
بين suspend/resume
end note
@enduml

ملخص أوامر الوظائف

طلب تأثير مثال

Ctrl+Z

إيقاف العملية في المقدمة

إرسال`SIGTSTP`

fg

استأنف المهمة الافتراضية في المقدمة

fg ou fg %1

fg %n

استأنف العمل n في المقدمة

fg %2

bg

استأنف الوظيفة الافتراضية في الخلفية

bg ou bg %1

bg %n

استأنف المهمة 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 "إيقاف العملية\n(هو لا يزال في الذاكرة)\nيمكن استخدامه مع fg/bg" as c1
}

rectangle "**Ctrl+C**\nSIGINT" as ctrlc #LightCoral {
  card "قتل العملية\n(لا يمكن العودة)\nضياع الحالة" as c2
}

rectangle "**& **\n(في الخلفية)" as ampersand #LightGreen {
  card "ابدأ مباشرة\nفي الخلفية\nالطرفية متاحة فورًا" as c3
}

rectangle "**fg**\n(الواجهة الأمامية)" as fg_ #SkyBlue {
  card "استعادة المهمة\nمعلق في الواجهة الأمامية\nالطرفية مشغولة" as c4
}

rectangle "**bg**\n(في الخلفية)" as bg_ #Lavender {
  card "استئناف المهمة\nمعلقة في الخلفية\nالطرفية حرة" as c5
}

ctrlz -right-> fg_ : fg لاستئناف
ctrlz -right-> bg_ : bg للمتابعة\nفي الخلفية
ctrlc -right-> c2 : لا عودة\nالعملية قُتِلَت

note bottom of ctrlz
العملية **مجمّده** لكن **ليس مقتولًا**
end note

note bottom of ampersand
Alternative : تشغيل
./gradlew build &
مباشرة في الخلفية
end note
@enduml
اختصار/أمر إشارة تأثير العودة للخلف؟

Ctrl+Z

SIGTSTP

أوقف العملية (متجمد، في الذاكرة)

نعم :`fg` ou bg

Ctrl+C

SIGINT

اقتل العملية (مكتملة)

لا: عملية مدمّرة

Ctrl+\

SIGQUIT

قتل العملية + تفريغ الذاكرة

لا : عملية مدمرة

commande &

—

شغل في الخلفية مباشرة

`fg`لإعادته إلى المقدمة

Ctrl+Z`هوغير اتلافي. العملية متجمدة كما هي، في الذاكرة. يمكنك المتابعة فورًا مع`fg. هذه pause، ليس stop

الدورة الكاملة لحياة الوظيفة

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

title دورة حياة كاملة لعملية شِل
state "غير موجود" as none
state "الامامية\n(occupe le terminal)" as foreground #LightCoral
state "معلق\n(Ctrl+Z / SIGTSTP)" as suspended #LightYellow
state "الخلفية\n(bg ou &)" as background #LightGreen
state "منتهي\n(exit code 0)" as done #LightGray

[*] --> none
none --> foreground : اكتب أمرًا
foreground --> suspended : Ctrl+Z
foreground --> done : نهاية عادية

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

background --> foreground : فج
background --> done : النهاية الطبيعية\n(en arrière-plan)
background --> suspended : Ctrl+Z\n(si fg puis Ctrl+Z)

done --> [*]

note right of suspended
الحالة المعلقة هي
المحور الرئيسي
لكل نظام الوظائف
end note
@enduml

نصائح متقدمة

ذكر/أنثى مع الجوب الأحدث والتناوب

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)

désown : فصل عن القشرة

disown`يزيل وظيفة من جدول وظائف القذيفة. تستمر العملية في التشغيل لكن لا يمكن إعادتها مع`fg :

$ ./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 "فج" as fg_ #SkyBlue {
  card "جيب الوظيفة\nالأحدث (+)" 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 "افصل من الغلاف\n(survit à la déconnexion)" as d1
}

rectangle "nohup + &" as nohup_ #LightGreen {
  card "العملية المناعية\nضد SIGHUP\n(survit à la fermeture\ndu terminal)" as n1
}

fg_ -right-> kill_ : إذا يجب\nإنهاء
kill_ -right-> disown_ : أو فكّ
nohup_ -left-> fg_ : fg يسترجع إلى\nالمقدمة

note bottom of disown_
بعد disown، لا يمكن لـ fg إرجاع الوظيفة.
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`تعمل فقط في الشل الذي أطلق فيه العمليات. إذا فتحت طرفية جديدة، فلن ترى وظائف الشل الآخر.

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

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

لرؤية عمليات النظام على مستوى النظام، استخدم`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 "الـTerminal متوقف\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 "jobs → عرض المعلقين" as s4
}

rectangle "**المزايا**" as advantages #SkyBlue {
  card "لا فقدان للسياق" as a1
  card "لا إعادة تشغيل" as a2
  card "يكفي طرفية אחת" as a3
}

problem -right-> solution : "الحل الأصلي\nUnix منذ 40 سنة"
solution -right-> advantages : "النتيجة"

note bottom of advantages
fg ليست نصيحة غامضة
إنها آلية أساسية
في شيل يونكس
end note
@enduml

لماذا fg غير مُقدر؟

معظم المطورين الحديثين يكتشفون`fg`متأخرًا، أو حتى أبدًا. بعض الأسباب:

  1. تخفي بيئات التطوير المتكاملة الطرفية: VS Code يقدم محطة طرفية مدمجة، لكن علامات التبويب المتعددة تجعل`fg`أقل ضرورة ظاهري

  2. الأدوات الرسومية تحل محل واجهات سطر الأوامر: مديري الملفات, مراقبو النظام, عملاء 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 "بيئة تطوير متكاملة مع طرفية\nمتعددة التبويبات" as w1
  card "أدوات رسومية\nتحل محل واجهة سطر الأوامر (CLI)" as w2
  card "tmux/screen\nيحلّان المشكلة\nبطريقة أخرى" as w3
}

rectangle "لماذا fg هو\nأفضل؟" as superior #LightGreen {
  card "عالمي: bash،\nzsh، fish، sh..." as s1
  card "بدون تبعية:\nبدون تثبيت" as s2
  card "ضغطتان:\nCtrl+Z ثم fg" as s3
  card "السياق محفوظ:\nصفر فقدان للحالة" as s4
}

why -right-> superior : "البساطة\nتفوز دائمًا"
@enduml

الذاكرة التقنية : الإشارات

للمتحمسين، هذه هي الإشارات المضمنة:

إشارة اسم أُرسل بواسطة التأثير

2

SIGINT

Ctrl+C

مقاطعة — أنهي العملية

3

SIGQUIT

Ctrl+\

اخرج مع تفريغ الذاكرة

18

SIGTSTP

Ctrl+Z

إيقاف تفاعلي — إيقاف العملية

19

SIGCONT

fg/bg

استأنف — يستأنف عملية معلقة

15

SIGTERM

kill %n

إنهاء نظيف

9

SIGKILL

kill -9 %n

إنهاء قسري (غير قابل للاعتراض)

التدفق الكامل:

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultFontName sans-serif

participant "مستخدِم" as user
participant "صدفة" as shell
participant "عملية" as proc

user -> shell : Ctrl+Z
shell -> proc : SIGTSTP (الإشارة 18)
note right of proc : العملية المتجمدة\n في الذاكرة

user -> shell : fg
shell -> proc : SIGCONT (إشارة 19)
note right of proc : العملية تستأنف\nفي المقدمة

user -> shell : bg
shell -> proc : SIGCONT (إشارة 19)
note right of proc : العملية تستأنف\n في الخلفية

user -> shell : Ctrl+C
shell -> proc : SIGINT (إشارة 2)
note right of proc : تم الانتهاء من العملية\nالحالة المفقودة
@enduml

خاتمة

`fg`ليس نصيحة غامضة — بل هو آلية أساسية في شل يونكس، متوفر منذ السبعينيات. أن يتجاهل معظم المطورين هذا الأمر هو أحد أعراض عصرنا: بيئات التطوير المتكاملة ومعدّلات المستخدمين أبعدتنا عن الأساسيات.

لكن عندما تكون على خادم SSH، في طرفية بسيطة، أو ببساطة أثناء الاستخدام`opencode`وأن نموذج اللغة الكبير في حالة عمل كامل —Ctrl+Z, fg, bg, `jobs`هم أصدقاؤكم الأفضلاء.

_ Ctrl+Z يوقف، fg يستأنف. هذا هو زر الإيقاف المؤقت-التشغيل للمحطة. _

مقالات ذات صلة