نصيحة ممتازة لأمر fg : عندما ينقذ Ctrl+Z جلسة الطرفية الخاصة بك
نُشر في 18 April 2026
- المشكلة: طرفية مشغولة
- الثلاثة الأوامر الضرورية
- سيناريو 1 : البناء الطويل
- سيناريو 2 : المحرر منسي
- السيناريو 3: عملية opencode معلقة
- السيناريو 4: إدارة وظائف متعددة في وقت واحد
- الاختلافات بين fg, bg, Ctrl+Z, Ctrl+C و &
- الدورة الكاملة لحياة الوظيفة
- نصائح متقدمة
- المصائد والأخطاء الشائعة
- مخطط ملخص
- لماذا fg غير مُقدر؟
- الذاكرة التقنية : الإشارات
- خاتمة
- المراجع
أنت تطلق`./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é
الميزة مزدوجة:
-
لا فقدان للسياق: جلسة LLM، سجل المحادثة، الملفات المفتوحة — كل شيء محفوظ
-
لا إعادة تشغيل: لا حاجة لإعادة تشغيل 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
ملخص أوامر الوظائف
| طلب | تأثير | مثال |
|---|---|---|
|
إيقاف العملية في المقدمة |
إرسال`SIGTSTP` |
|
استأنف المهمة الافتراضية في المقدمة |
|
|
استأنف العمل n في المقدمة |
|
|
استأنف الوظيفة الافتراضية في الخلفية |
|
|
استأنف المهمة n في الخلفية |
|
|
قائمة الوظائف الخاصة بالقشرة الحالية |
|
|
أنهِ العمل n |
|
الاختلافات بين 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
| اختصار/أمر | إشارة | تأثير | العودة للخلف؟ |
|---|---|---|---|
|
|
أوقف العملية (متجمد، في الذاكرة) |
نعم :`fg` ou |
|
|
اقتل العملية (مكتملة) |
لا: عملية مدمّرة |
|
|
قتل العملية + تفريغ الذاكرة |
لا : عملية مدمرة |
|
— |
شغل في الخلفية مباشرة |
`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`متأخرًا، أو حتى أبدًا. بعض الأسباب:
-
تخفي بيئات التطوير المتكاملة الطرفية: VS Code يقدم محطة طرفية مدمجة، لكن علامات التبويب المتعددة تجعل`fg`أقل ضرورة ظاهري
-
الأدوات الرسومية تحل محل واجهات سطر الأوامر: مديري الملفات, مراقبو النظام, عملاء Git الرسوميون
-
tmux و screen: هذه المتعدِّدات تحل نفس المشكلة بطريقة مختلفة، لكن`fg`هو أكثر بساطة ومتوفر دائمًا
ومع ذلك،fg:
-
عالمي: موجود في جميع أصداف POSIX (bash, zsh, fish…)
-
بدون اعتمادلا تحتاج إلى تثبيت tmux أو screen
-
لحظي: ضربتان (
Ctrl+Zثم`fg`) لإيقاف وإعادة التشغيل -
حافظ السياقحالة العملية سليمة
@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 |
|
|
مقاطعة — أنهي العملية |
3 |
|
|
اخرج مع تفريغ الذاكرة |
18 |
|
|
إيقاف تفاعلي — إيقاف العملية |
19 |
|
|
استأنف — يستأنف عملية معلقة |
15 |
|
|
إنهاء نظيف |
9 |
|
|
إنهاء قسري (غير قابل للاعتراض) |
التدفق الكامل:
@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 يستأنف. هذا هو زر الإيقاف المؤقت-التشغيل للمحطة. _
المراجع
-
man bash— القسم JOB CONTROL -
man signal— قائمة إشارات POSIX