الغوص في الأنظمة › 17. التذييل 2: استخدام Unix 18 / 18 السابق

17. الملحق 2: استخدام Unix

يقدّم هذا الملحق نظرة عامة على استخدام سطر أوامر Unix وبعض أوامر وأدوات Unix المفيدة. وتركيزنا على Linux، الذي يمثّل واجهة سطر الأوامر لمجموعة أوسع من أنظمة تشغيل Unix. وقد تكون بين تنفيذات Unix المختلفة فروق طفيفة، لكن كل ما نعرضه هنا تقريبًا متماثل عبر تنفيذات Unix المختلفة. وتهدف تغطيتنا إلى تقديم مقدمة لهذه الموضوعات، مع إحالتك إلى تغطية أكثر تقدمًا وعمقًا في مواضع أخرى. وتشمل الموضوعات التي نغطيها ما يلي:

  • أنظمة ملفات Unix واستخدام سطر أوامر Unix (تشمل الأوامر: ls وpwd وmkdir وcp وmv وrm وrmdir وcat وecho وwc وtouch).
  • دليل Unix وقراءة صفحات man في Unix (man وapropos).
  • الوصول عن بُعد إلى أنظمة Unix (ssh وscp).
  • محررات النصوص في Unix مع بعض المعلومات عن تعلّم المحرر vim (وvi).
  • استخدام make وكتابة ملفات Makefile.
  • بعض ميزات وأدوات سطر الأوامر المتقدمة، ومنها: البحث في الملفات والمجلدات: grep وfind.
  • التحكم في العمليات وتشغيلها (ps و& وfg وbg وCNTRL-Z وkill وhistory وtime).
  • ملفات الأرشيف (tar) وضغط الملفات (gzip وbzip2).
  • صلاحيات الملفات (chmod وls -l).
  • إعادة توجيه الإدخال/الإخراج (, 2>, &>, >>) والأنابيب (|).
  • ملفات النقطة، و.bashrc، ومتغيرات البيئة
  • كتابة سكربتات صدفة Bash البسيطة واستخدامها.
  • أدوات للحصول على معلومات عن حالة النظام (top و/proc).

توفّر أنظمة Unix واجهة سطر أوامر ليتفاعل المستخدمون مع النظام. وعندما يسجّل مستخدم الدخول إلى نظام Unix، فإنه يتفاعل مع برنامج صدفة (shell) يعمل في نافذة طرفية. وتقرأ الصدفة الأوامر التي يُدخلها المستخدم وتنفّذ الإجراء المطلوب. ومن أمثلة برامج الصدفة bash وzsh. وتعرض الصدفة مطالبة (مثل $) ويكتب المستخدم أمرًا ينفّذه برنامج الصدفة. وقد يكون القراء (خصوصًا من يعرفون Python) ملمّين بحلقة القراءة-التقييم-الطباعة (REPL)، وهي بيئة شبيهة بالصدفة تُنفَّذ في سياق لغة برمجة. ومثل REPL، تقرأ واجهة سطر أوامر الصدفة أمرًا وتقيّمه وتطبع النتائج ثم تعود إلى مطالبة الصدفة، في حلقة. وخلافًا لـREPL، تتيح واجهة سطر أوامر الصدفة للمستخدمين الوصول إلى واجهة نظام التشغيل وتشغيل مجموعة متنوعة من البرامج المختلفة. فمثلًا، سيخبرك الأمر whoami باسم المستخدم لديك:

$ whoami
myusername

يخزّن نظام ملفات Unix البرامج والملفات الأخرى. ويكتب المستخدمون أوامر عند مطالبة الصدفة لتشغيل البرامج والتنقل في نظام ملفات Unix ومعالجة الملفات.

ولكثير من الأوامر خيارات سطر أوامر (command line options) تحدّد خيارات مختلفة أو سلوكيات معيّنة للأمر. كما تأخذ أوامر كثيرة وسائط سطر أوامر (command line arguments) تُستخدم لتحديد هدف العملية. فمثلًا، cp هو أمر النسخ الذي ينسخ ملفًا إلى آخر. ويأخذ وسيطي سطر أوامر، هما اسم الملف المصدر المراد نسخه واسم الملف الهدف الذي سيحتوي النسخة.

17.1.1. نظام ملفات Unix

يتكوّن نظام ملفات Unix من ملفات وأدلة. ويخزّن الملف بيانات مثل شيفرة البرامج المصدرية وبيانات إدخال البرامج وملاحظات المحاضرات وشيفرة الملفات التنفيذية الثنائية. وتخزّن الأدلة الملفات والأدلة الفرعية وتُستخدم لتنظيم مجموعة الملفات المخزّنة في نظام الملفات.

ونظام ملفات Unix منظّم في بنية شجرية هرمية، بدليل واحد في الأعلى (/) يُسمى الدليل الجذر. وتحته بعض الأدلة الرئيسية التي تحمل محتوى النظام. ومن الأمثلة عليها bin الذي يخزّن برامج النظام التي يمكن تشغيلها من سطر أوامر Unix، وlib الذي يخزّن شيفرة مكتبات النظام، وhome الذي يخزّن الدليل الرئيسي لكل مستخدم ليخزّن فيه ملفاته الخاصة. ويعرض الشكل 1 مثالًا على بنية أدلة نظام ملفات Unix، مع دليلي home للمستخدمين sam وsarita.

نظام ملفات Unix بالدليل الجذر في الأعلى، مع أدلة فرعية نموذجية هي bin وhome وlib، ودليلَين فرعيَّين نموذجيَّين تحت home هما sam وsarita. الشكل 1. نظام ملفات Unix منظّم كهرمية يكون الدليل الجذر في أعلاها، وتُرتَّب الأدلة الفرعية والملفات تحته.

لاحظ أن / هو الدليل الأعلى مستوى، وله عدة أدلة فرعية تابعة، منها bin وlib وhome. ولكل دليل عدا الدليل الجذر دليل أب فريد؛ والدليل الجذر هو الوحيد الذي ليس له دليل أب.

ولكل مستخدم في النظام دليله الرئيسي الخاص، وهو دليل فرعي في /home. ويحمل الدليل الرئيسي لكل مستخدم الاسم نفسه الذي يحمله اسم المستخدم. وفي الشكل نعرض بعض الأدلة الرئيسية كمثال. ولكل دليل عدا / دليل أب واحد بالضبط. فمثلًا، home هو الدليل الأب لـsarita، و/ هو الدليل الأب لـhome. ويُعرَّف كل دليل بـاسم مسار (pathname) فريد يبدأ من الدليل الجذر. فمثلًا، /home/sarita هو اسم مسار الدليل الرئيسي للمستخدم sarita: يبدأ اسم المسار من الدليل الجذر /، ثم يتبع الدليل الفرعي home في /، وأخيرًا الدليل الفرعي sarita في home.

وعند تسجيلك الدخول إلى نظام Unix لأول مرة أو بدء برنامج صدفة جديد، تبدأ في دليلك الرئيسي. ويطبع الأمر pwd (طباعة دليل العمل) الدليل الذي أنت فيه حاليًا، مع عرض اسم مساره الكامل بدءًا من الدليل الجذر /. فمثلًا، هذه هي المخرجات عندما يسجّل المستخدم sarita الدخول ويكتب pwd:

$ pwd
/home/sarita

وعندما يسجّل المستخدم sam الدخول ويكتب pwd، تكون المخرجات:

$ pwd
/home/sam

17.1.2. أوامر نظام الملفات

تتيح أوامر Unix للمستخدمين التنقل في بنية أدلتها، وإنشاء الملفات والأدلة وحذفها، وسرد محتويات دليل، ومعرفة الدليل الذي هم فيه حاليًا. وجميع الإجراءات نسبية إلى دليل العمل الحالي. فمثلًا، إذا أنشأ مستخدم ملفًا جديدًا، يُنشأ الملف في دليل العمل الحالي للمستخدم.

ويغيّر الأمر cd (تغيير الدليل) دليل العمل الحالي (أو ينتقل إلى دليل مختلف). ويسرد الأمر ls (سرد) محتويات الدليل الحالي. ويُستخدم الأمر mkdir لإنشاء دليل جديد.

فمثلًا، لإنشاء دليل فرعي اسمه unix_notes في دليلها الرئيسي، وللانتقال إلى ذلك الدليل، ستكتب sarita ما يلي:

$ mkdir unix_notes  # make a new directory named unix_notes
$ ls                # list contents of current directory
 unix_notes/
$ cd unix_notes     # move into the new directory
$ pwd               # print current working directory
/home/sarita/unix_notes

وتوجد طرق كثيرة لإنشاء ملفات جديدة. وغالبًا ما ينشئ المستخدمون ملفات جديدة باستخدام برنامج محرر. وثمة طريقة أقل شيوعًا لإنشاء ملف فارغ جديد هي استخدام الأمر touch:

$ touch basics
$ ls
 basics

ولأننا لا نعرف بعد أي برنامج محرر، يمكننا إضافة بعض المحتوى إلى الملف باستخدام الأمر echo وإلحاق خرجه بملف basics (>> تلحق خرج echo بالملف المحدّد، وهو basics في هذا المثال). فمثلًا:

$ echo "ls: list directory contents" >> basics
$ echo "cd: change directory" >> basics
$ echo "pwd: print working directory" >> basics

ويُفرغ الأمر cat محتويات ملف إلى الطرفية. ولمعرفة محتويات basic بعد تشغيل أوامر echo أعلاه:

$ cat basics
ls: list directory contents
cd: change directory
pwd: print working directory

ملاحظة — أسماء الملفات في Unix

لاحظ أن أسماء الملفات والأدلة في Unix حساسة لحالة الأحرف، فمثلًا basics وBasics اسمان لملفين مختلفين.

كما أنه رغم أن أسماء الملفات قد تحتوي محارف مسافة، يجب تحديدها باستخدام محرف الهروب قبل محرف المسافة (فمثلًا hello\ there هي طريقة تسمية الملف "hello there"). ونتيجة لذلك، لا ينشئ مستخدمو Unix عادةً ملفات تحتوي محارف مسافة في أسمائها. وبدلًا من ذلك، يستخدم المستخدمون _ أو - في المواضع التي يريدون فيها مسافة في الاسم (مثل hello_there).

ويعدّ الأمر wc عدد الكلمات والأسطر والبايتات في ملف. فمثلًا، لرؤية هذه المعلومات للملف الذي أنشأناه للتو، شغّل:

$ wc basics
 3      11      78 basics

وينظّم مستخدمو Unix عادةً دليلهم الرئيسي كمجموعة من الأدلة تُخزَّن تحتها أدلة فرعية وملفات مترابطة. فمثلًا، سيستخدم المستخدم sam الأمر mkdir لإنشاء ثلاثة أدلة فرعية في دليله الرئيسي، واحد لأعمال الدراسة وواحد للمشاريع وواحد للمراسلات الشخصية:

$ mkdir classes
$ mkdir projects
$ mkdir letters

$ ls
classes/    letters/   projects/

ولأن هذا المستخدم يدرس مقرر CS31، فهو يريد إنشاء دليل فرعي تحت دليله الفرعي classes لجميع أعماله في هذا المقرر. ولفعل ذلك سيقوم بما يلي:

$ cd classes
$ pwd
/home/sam/classes
$ mkdir CS31
$ ls
CS31/
$ cd CS31
$ pwd
/home/sam/classes/CS31

ويعرض الشكل 2 شكل نظام الملفات بعد أن يشغّل sam جميع أوامر mkdir.

نظام ملفات Unix بعد أن ينشئ sam الأدلة الفرعية classes وletters وprojects، وينشئ دليلاً فرعياً باسم CS31 تحت classes. الشكل 2. تنظيم نظام ملفات Unix بعد أن ينشئ المستخدم sam بعض الأدلة الفرعية.

والآن يمكن لـsam الانتقال إلى دليله CS31 عندما يعمل على دروس مقرر CS31، وإنشاء المزيد من الأدلة الفرعية والملفات في هذا الدليل:

$ cd CS31
$ pwd
/home/sam/classes/CS31

أسماء مسارات الملفات والتنقل في نظام الملفات

كما رأينا، يُستخدم الأمر cd للتنقل في نظام الملفات، ويسرد ls محتويات دليل.

ويأخذ كلٌّ من cd وls وسيط سطر أوامر واحدًا يحدّد الدليل المراد الانتقال إليه أو سرد محتوياته على الترتيب. وهناك طريقتان لتسمية موقع دليل لينتقل إليه cd. إحداهما سرد الدليل بـاسم مساره المطلق، أي اسمه بدءًا من الدليل الجذر (/)، والأخرى سرده بـاسم مساره النسبي، أي اسمه من دليل العمل الحالي.

ويمكنك أيضًا تشغيل cd وls دون وسيط اسم مسار. فتشغيل cd وحده سيعيدك إلى بيتك (دليلك الرئيسي)، وتشغيل ls وحده يسرد محتويات دليل العمل الحالي. وفيما يلي بعض الأمثلة التي جرّبها المستخدم sam:

$ cd /home/sam/classes/CS31   # cd with absolute path name
$ pwd
/home/sam/classes/CS31

$ cd                          # cd with no path name  (go home)
$ pwd
/home/sam/

$ ls                          # ls with no path name (list in current directory)
classes/    letters/   projects/

$ cd classes/CS31        # cd with relative path name
$ pwd
/home/sam/classes/CS31

$ ls classes/            # ls with relative path name
CS31/

$ cd /                   # cd with absolute path name (go to root directory)
$ pwd
/

وغالبًا ما يستخدم المستخدمون المسار النسبي لأنه أقصر في الكتابة عادةً (فهم ينتقلون غالبًا قرب مكانهم الحالي).

ويحتوي كل دليل على مدخلين مخفيين يساعدان على التنقل في الأدلة بأمر cd: . اسم آخر للدليل الحالي، و.. اسم آخر للدليل الأب للدليل الحالي. ويمكنك رؤية هذين الملفين مدرجين إذا استخدمت خيار سطر الأوامر -a مع ls:

$ pwd
/home/sam/classes/CS31
$ ls -a
.   ..

ويمكن استخدامهما في أسماء المسارات النسبية والمطلقة معًا، لتحديد الدليل الحالي أو دليل الأب. واستخدام .. في اسم مسار يعني الصعود دليلًا واحدًا.

$ pwd
/home/sam/classes/CS31

$ cd ..                 # move up one directory to parent directory
$ pwd
/home/sam/classes

$ cd /bin/../home/sam   # go to sam's home directory in an unusual way
$ pwd
/home/sam

$ cd ./classes/./..     # a wacky example of not going anywhere
$ pwd
/home/sam

وأخيرًا، ~ اختصار للأدلة في /home. وعند استخدامها وحدها، تكون ~ اختصارًا للدليل الرئيسي للمستخدم. وعند دمجها مع اسم مستخدم آخر، تشير ~ إلى الدليل الرئيسي لذلك المستخدم الآخر. وفيما يلي بعض الأمثلة التي شغّلها المستخدم sam:

$ cd ~/       # cd into home directory
$ pwd
/home/sam/

$ ls ~/
classes/    letters/   projects/

$ ls ~sarita/
unix_notes/

$ cd ~sartita/
$ pwd
/home/sarita

ملاحظة — عن المثال السابق

في كثير من الأحيان يضبط مدير النظام صلاحيات افتراضية للملفات والأدلة في home/ تمنع المستخدمين من رؤية الأدلة الرئيسية والملفات الخاصة ببعضهم. وفي نظام كهذا، قد لا يتسنى لـSam الانتقال بـcd إلى الدليل الرئيسي لـSarita، أو سرد ملفاتها بـls كما يفعل في هذا المثال.

نسخ الملفات ونقلها

ينسخ الأمر cp ملفًا (أو دليلًا) إلى آخر، وينقل الأمر mv ملفًا (أو دليلًا) من دليل إلى آخر، أو يعيد تسمية ملف. ويأخذ هذان الأمران وسيطي سطر أوامر، هما المصدر والهدف.

وفيما يلي بعض الأمثلة؛

$ cd unix_notes
$ cp basics test   # copy the basics file to a new file named test
$ ls
basics test

$ mv test ../blah  # move the test file one directory up and renames it to blah
$ ls
basics

$ cd ../
$ ls
blah   unix_notes/

$ cp -r unix_notes notes_copy  # recursively copy all contents of
                               # of unix_notes directory to new directory
                               # named notes_copy
$ cd notes_copy
$ pwd
/home/sarita/notes_copy
$ls
basics

$ cp basics ../.    # copy basics up a directory to file with same name (/.)
$ cd
$ pwd
/home/sarita/
$ ls
basics unix_notes

إزالة الملفات والأدلة

أحيانًا تريد إزالة ملفات وأدلة. ويُستخدم الأمر rm لإزالة ملف، ويُستخدم rmdir لإزالة دليل. ويأخذ كلاهما اسم الملف أو الدليل المراد إزالته. ولكل منهما خيارات سطر أوامر تحدّد سلوك الإزالة.

تحذير — إزالة الملفات دون خيار سطر الأوامر -i

يحذف الأمر rm افتراضيًا الملف حذفًا نهائيًا. فهو لا ينقله إلى سلة محذوفات كما قد تفعل حذف الملفات في بعض أنظمة التشغيل الأخرى. لذلك ينبغي استخدام rm بحذر. وأحيانًا يضبط مدير النظام rm ليستخدم خياره -i تلقائيًا بشكل افتراضي، بحيث يطبع rm أولًا مطالبة لإعطاء المستخدم فرصة إدخال n قبل إزالة الملف (وإذا أدخل المستخدم y أو Y في المطالبة فسيُزال الملف، وأي إجابة أخرى فلن يُزال).

وفي الأنظمة التي لا يكون فيها rm مضبوطًا لتشغيل rm -i افتراضيًا، يضيف المستخدمون غالبًا هذه الميزة إلى ملف تهيئة الصدفة لديهم. ونناقش ملفات تهيئة Bash بمزيد من التفصيل في القسم 17.6.

فمثلًا (لاحظ أنه إذا لم يكن نظامك مضبوطًا بحيث يستخدم rm الخيار -i افتراضيًا، فستكتب rm -i لترى السلوك نفسه).

$ ls
 basics
$ rm basics
  rm: remove regular file basics? y
$ ls

وبشكل افتراضي، لا يزيل rmdir إلا الأدلة الفارغة، وهي ميزة تمنع المستخدم من إزالة محتوى لم يكن يقصد إزالته عن غير قصد، بإجباره على إزالة جميع محتويات الدليل صراحةً قبل إزالة الدليل. وفيما يلي بعض الأمثلة على استدعاءات من المستخدم sam:

$ cd
$ pwd
/home/sam
$ ls
classes/    letters/   projects/
$ rmdir letters
$ ls
classes/    projects/

$ rmdir classes
rmdir: failed to remove classes: Directory not empty
$ ls
classes/    projects/

ويمكن للمستخدم إزالة محتويات دليل إزالة تعاودية باستخدام rm -r، كما يمكنه إزالة الملفات قسرًا (بإلغاء كل المطالبات قبل إزالة الملفات) بإضافة الخيار -f أيضًا: rm -rf.

تحذير — كن حذرًا جدًا مع خياري سطر الأوامر -f و-r

ينبغي توخي الحذر الشديد عند استخدام -f مع rm، لأن سلوك المطالبة في rm -i غالبًا ما يمنع الإزالة العرضية للملفات؛ فإذا شغّلت rm -rf فقد تُزيل دليلًا كاملًا من الملفات إزالة تعاودية عن غير قصد. ولهذا، من الأفضل تجنّب استخدام خياري سطر الأوامر هذين. غير أنهما مفيدان عند حذف عدد كبير من الملفات والأدلة الفرعية. وفي الحالات التي تريد فيها استخدام هذين الخيارين، من الممارسات الجيدة أن تتأكد أولًا من أنك في الدليل الصحيح (بتشغيل ls وpwd) قبل تشغيل rm -rf.

17.1.3. ملخص الأوامر الأساسية

وفيما يلي ملخص لبعض أوامر UNIX التي نوقشت في هذا القسم، مع أمثلة:

pwd — طباعة دليل العمل: يطبع اسم المسار الكامل للدليل الحالي (cwd).

cd — تغيير الدليل: يغيّر الدليل الحالي ليكون الدليل المحدّد بـ``. ويمكن أن يكون اسم المسار نسبيًا إلى دليل العمل الحالي أو مسارًا مطلقًا من /.

للانتقال إلى الدليل sub1 من الدليل الحالي:

cd sub1

للانتقال إلى الدليل /home/sam بغض النظر عن الدليل الحالي:

cd /home/

للتغيير إلى الدليل الأب للدليل الحالي (أي الصعود مستوى واحدًا في هرمية نظام الملفات):

cd ..

للتغيير إلى دليل "البيت" الخاص بك:

cd

ls — السرد: يسرد الملفات والأدلة في الدليل الحالي.

ls .

لسرد محتويات الدليل الرئيسي للمستخدم sam:

ls /home/sam

mv — النقل: ينقل/يعيد تسمية الملف المحدّد باسم المسار إلى.

لإعادة تسمية oldfile إلى newfile

mv oldfile newfile

لنقل newfile إلى الدليل الفرعي sub1:

mv newfile sub1/

لإعادة تسمية الدليل sub1 إلى sub2:

mv sub1 sub2

cp — النسخ: أنشئ نسخة جديدة من الملف المحدّد باسم المسار وسمِّ النسخة الجديدة.

لنسخ oldfile إلى newfile (يبقى oldfile موجودًا):

cp oldfile newfile

لنسخ oldfile دليلًا إلى الأعلى إلى ملف اسمه oldfile أيضًا (يوجد في الدليل الحالي والدليل الأب ملف اسمه oldfile، ومحتواهما متطابق إلى أن يُعدَّل أحد الملفين):

cp oldfile ../.

rm — الإزالة: احذف الملف المحدّد بـ``.

لحذف ملف اسمه temp في دليلي الرئيسي:

rm ~/temp

mkdir — إنشاء دليل: أنشئ دليلًا جديدًا اسمه ``.

لإنشاء دليل جديد اسمه test في الدليل الحالي:

mkdir test

لإنشاء دليل جديد اسمه private في دليلي الرئيسي:

mkdir ~/private

rmdir — إزالة دليل: احذف الدليل المحدّد باسم المسار `` (يجب أن يكون فارغًا).

لإزالة دليل اسمه private في دليلي الرئيسي:

rmdir ~/private

wc — عدّ الكلمات: اسرد عدد البايتات والكلمات والأسطر في ملف.

touch — إنشاء ملف فارغ جديد باسم ``.

لإنشاء ملف فارغ جديد اسمه my_notes في الدليل الحالي:

touch my_notes

cat — اطبع محتويات `` إلى الطرفية.

لطباعة محتويات ملف basics:

cat basics

less — اطبع محتويات `` إلى الطرفية، صفحة واحدة في المرة، مع التوقف عند كل صفحة. وينتقل مفتاح المسافة إلى الصفحة التالية.

لطباعة محتويات ملف basics صفحة واحدة في المرة:

less basics

more — مبرّد صفحات آخر (مثل less) لعرض الملفات صفحة واحدة في المرة.

17.1.4. المراجع

لمزيد من المعلومات انظر:

توفّر صفحات دليل Unix توثيقًا لأوامر Unix مثل ls أو rm، ولنداءات النظام، ودوال المكتبات. ويمكنك قراءة صفحات الدليل باستخدام أمر Unix المسمّى man. ويمكن استخدام الأمر apropos للبحث عن أوامر Unix ودوال المكتبات المتعلقة بموضوع يوفّره نمط بحث يُعطى كوسيط سطر أوامره. كما تتوفر كثير من صفحات دليل Unix ومكتبة C على الإنترنت أيضًا، مثلًا على kernel.org.

وينقسم دليل Unix إلى عدة أقسام. والأقسام الثلاثة الأولى هي الأكثر استخدامًا.

  1. القسم 1 للأوامر التي يمكنك إدخالها على سطر الأوامر (مثل ls).
  2. القسم 2 لنداءات نظام Unix التي يمكنك استدعاؤها من شيفرة البرنامج (مثل fork).
  3. القسم 3 لدوال المكتبات التي يمكنك استدعاؤها من شيفرة برنامجك (مثل دوال مكتبة C كـprintf).

شغّل الأمر man لعرض صفحة الدليل التي تحتوي توثيقًا عن أمر أو دالة. وتُفتح صفحة الدليل في برنامج اسمه less. ويمكنك الخروج من less بكتابة q، أو النزول صفحة كاملة بكتابة مفتاح المسافة، أو الصعود والنزول سطرًا واحدًا في المرة بمفاتيح الأسهم. ويمكنك أيضًا البحث عن محتوى باستخدام صيغة / المشابهة للبحث في Vim.

لعرض صفحة دليل الأمر ls، شغّل:

$ man ls

وأحيانًا يُستخدم الاسم نفسه لأمر ودالة. وفي هذه الحالة يمكنك الحصول على صفحة الدليل الصحيحة بتحديد الجزء من الدليل الذي تحتويه. فمثلًا، للحصول على صفحة دليل نداء النظام write، الذي يظهر في القسم 2 من الدليل، اكتب:

$ man 2 write

ويمكنك أيضًا قراءة صفحة دليل man لمعرفة المزيد عنه:

$ man man

17.2.1. قراءة صفحات man

قد لا يكون من الواضح تمامًا كيفية قراءة صفحة دليل، فلننظر إلى مثال على صفحة دليل toupper:

$ man toupper

وتبدو صفحة دليل دالة المكتبة toupper شبيهة بما يلي:

TOUPPER(3)          Linux Programmer's Manual          TOUPPER(3)

NAME
       toupper, tolower - convert letter to upper or lower case

SYNOPSIS
       #include  <ctype.h>

       int toupper (int c);
       int tolower (int c);

DESCRIPTION
       toupper()  converts  the letter c to upper case, if possible.

       tolower() converts the letter c to lower case,  if  possible.

       If  c is not an unsigned char value, or EOF, the behavior
       of these functions is undefined.

RETURN VALUE
       The value returned is that of the converted letter,  or  c
       if the conversion was not possible.

CONFORMING TO
       ANSI C, BSD 4.3

BUGS
       The  details of what constitutes an uppercase or lowercase
       letter depend on the current  locale.   For  example,
       the default C locale does not know about umlauts, so
       no conversion is done for them.
       In some non-English locales, there are lowercase letters
       with  no  corresponding  uppercase  equivalent; the German
       sharp s is one example.

SEE ALSO
       isalpha(3), setlocale(3), locale(7)

GNU                   1993-04-04                 TOUPPER(3)

يقول السطر الأول، TOUPPER(3)، إن صفحة دليل toupper في القسم 3 من الدليل.

وتعطي أجزاء NAME وDESCRIPTION وRETURN VALUE أوصافًا قصيرة وطويلة لما تفعله الدالة، وما معاملاتها، وما تعيده:

NAME
       toupper, tolower - convert letter to upper or lower case

DESCRIPTION
       toupper()  converts  the letter c to upper case, if possible.

RETURN VALUE
       The value returned is that of the converted letter,
       or  c if the conversion was not possible.

ويشير جزء SYNOPSIS إلى ما إذا كانت هناك حاجة إلى تضمين أي ملفات ترويسة لاستخدام هذه الدالة، وإلى ما إذا كان من الضروري ربط أي مكتبات صراحةً لترجمة ملف تنفيذي. كما يسرد النموذج الأولي الكامل للدالة:

SYNOPSIS
       #include < ctype.h >

       int toupper (int c);

ويشير الملخص إلى أنه لاستدعاء toupper يجب أن يتضمّن ملف .c العبارة #include ، وأنه لا حاجة إلى ربط أي مكتبة صراحةً ببرنامج يستدعي هذه الدالة.

ويسرد جزء SEE ALSO دوال أخرى مرتبطة. ويمكنك بعد ذلك تشغيل man عليها لمعرفة المزيد عنها (مثل man isalpha).

SEE ALSO
       isalpha(3), setlocale(3), locale(7)

وتتضمّن صفحات الدليل أحيانًا أمثلة على كيفية استدعاء الدالة.

17.2.2. apropos

أحيانًا لا تعرف اسم دالة أو أمر تريد استخدامه. ويبحث الأمر apropos في أوصاف صفحات دليل Unix عن كلمات مفتاحية تطابق نمط البحث الذي تسرده كخيار سطر أوامر، ويسرد الأوامر/الدوال التي يرى أنها مطابقات مناسبة. فمثلًا، إذا أردت معرفة اسم دالة للحصول على دليل العمل الحالي، لكنك لا تتذكر اسم الدالة، يمكنك سؤال apropos:

$ apropos "current working directory"
get_current_dir_name (3) - Get current working directory
getcwd (3)               - Get current working directory
getwd (3)                - Get current working directory

ثم يمكنك قراءة بعض صفحات الدليل الخاصة بالنتائج لمعرفة أيّها تريد:

$ man 3 getcwd

17.2.3. المراجع

لمزيد من المعلومات انظر:

  • صفحات الدليل: man man وman apropos
  • kernel.org النسخة على الإنترنت من صفحات دليل Linux.

غالبًا ما يريد المستخدمون تسجيل الدخول عن بُعد إلى أجهزة Unix أو نسخ الملفات من جهاز إلى آخر. وSecure Shell (SSH) مفيد لتسجيل الدخول عن بُعد إلى جهاز Unix من جهاز آخر يعمل بـUnix أو بنظام تشغيل آخر مثل Windows وMacOS. وSecure Copy (SCP) مفيد لنسخ الملفات من نظام إلى آخر.

17.3.1. تسجيل الدخول عن بُعد

يمكنك تسجيل الدخول إلى جهاز Unix بعيد باستخدام الأمر ssh. ويوفّر ssh تواصلًا مشفّرًا بين الجهاز المضيف الذي تشغّل منه ssh والجهاز البعيد الذي اتصلت به.

وللدخول عبر SSH إلى جهاز بعيد، اسرد اسم المستخدم على الجهاز البعيد متبوعًا باسم المضيف على الإنترنت للجهاز. فمثلًا، إذا أرادت Sarita تسجيل الدخول عن بُعد إلى حسابها sarita على الجهاز cs87 في شبكة cs.college.edu من حاسوبها المحمول، فستُدخل الأمر التالي (لتوضيح أي جهاز ينفّذ كلًّا من هذه الأوامر، فإن المطالبة laptop$ هي مطالبة الصدفة على حاسوب Sarita المحمول، والمطالبة cs87$ هي مطالبة الصدفة على الجهاز البعيد).

laptop$ ssh sarita@cs87.cs.college.edu

وإذا نجح الاتصال، سيطلب خادم ssh العامل على الجهاز البعيد من المستخدم إدخال كلمة مروره على ذلك الجهاز. وبعد نجاح تسجيل الدخول عن بُعد، يبدأ ssh صدفة Unix على الجهاز البعيد يستطيع المستخدم من خلالها الوصول إلى الملفات وتشغيل التطبيقات على النظام البعيد.

laptop$ ssh sarita@cs87.cs.college.edu
sarita@cs.college.edu's password:
cs87$

الاتصال عن بُعد بـSSH من أنظمة تشغيل غير Unix

للدخول بـssh إلى نظام Unix من نظام غير Unix قد تحتاج أولًا إلى تثبيت عميل SSH على جهازك، لكنه مرجّح أن يكون مثبّتًا بالفعل. ثم تحتاج إلى نافذة طرفية لتشغيل ssh. وعلى MacOS، افتح تطبيق الطرفية واستخدمه. وعلى أجهزة Windows، يمكنك إما استخدام PowerShell وإما تثبيت Putty واستخدامه.

17.3.2. نسخ الملفات عن بُعد

إذا أردت نقل الملفات إلى جهاز على نظام بعيد أو منه، يمكنك استخدام scp (النسخ الآمن). ويبدو الأمر scp شبيهًا جدًا بالأمر cp إلا أن أحد الطرفين، المصدر أو الهدف، يُحدَّد كاسم مسار على جهاز بعيد. ومثل ssh، يتطلب scp من المستخدم كتابة كلمة مرور المستخدم على الجهاز البعيد للسماح بالوصول إلى الملفات المنسوخة إليه أو منه على النظام البعيد. وإذا نجح scp، فإنه يعرض بعض الإحصاءات عن الملف المنسوخ، منها حجم الملف بالبايتات ومتوسط معدل النقل وزمن النقل الإجمالي.

فمثلًا، لنسخ الملف المسمّى prog.c من جهازك الرئيسي إلى المستخدم sam على الجهاز cs87.cs.college.edu، ستكتب:

# scp prog.c into home directory on cs87.cs.college.edu
laptop$ scp prog.c sam@cs87.cs.college.edu:.
sam@cs.college.edu's password:
prog.c                  100%  282    56.4KB/s   00:00

# scp prog.c into CS31 subdirectory on cs87.cs.college.edu
laptop$ scp prog.c you@cslab.cs.college.edu:./CS31/.
sam@cs.college.edu's password:
prog.c                  100%  282    56.4KB/s   00:00

ويمكنك أيضًا نسخ الملفات من جهاز بعيد إلى جهاز محلي باستخدام اسم المسار البعيد كوسيط سطر الأوامر المصدر لـscp. فمثلًا، لنسخ ملف بعيد unix_notes/basics من ملفات المستخدم sarita على النظام البعيد إلى الدليل الحالي على جهازك الرئيسي، نفّذ:

laptop$ scp sarita@cs87.cs.college.edu:./unix_notes/basics ./
sarita@cs.college.edu's password:
basics                  100%   78   86.3KB/s   00:00

وإذا أردت نقل ملفات كثيرة، فمن المفيد أن تحزمها أولًا في ملف أرشيف واحد. ثم تنسخ ملف الأرشيف، وتفكّ ضغطه للحصول على مجموعة الملفات. علاوة على ذلك، سيقلل ضغط الملف قبل النقل من زمن النقل الإجمالي. وأداة tar إحدى طرق حزم الملفات وفكّها، وgzip وbzip2 مثالان على أدوات ضغط الملفات. انظر targzbz لمزيد من المعلومات عن استخدام tar وضغط الملفات.

إنشاء مفاتيح SSH

للاتصال عن بُعد بجهاز Linux، لا تحتاج إلى إنشاء مفاتيح ssh؛ فـssh يستخدم افتراضيًا اسم المستخدم وكلمة المرور لديك على النظام البعيد الذي تتصل به للمصادقة. غير أن بعض المواقع، مثل GitHub، قد تتطلب منك رفع مفتاح ssh العام الخاص بك إلى خادم الويب الخاص بها للوصول إلى بعض مواردها. ويستخدم ssh تشفير المفتاح العام-الخاص لتشفير البيانات المراد إرسالها بأمان عبر اتصال شبكي. ويُعرف نوع التشفير الذي يستخدمه ssh بالتشفير غير المتماثل، حيث يُستخدم مفتاحك الخاص على جهازك المضيف (العميل) لتشفير البيانات الصادرة المرسلة عبر الشبكة إلى خادم، ويستخدم الخادم مفتاحك العام لفكّ تشفير رسالتك. ويشفّر الخادم ردّه باستخدام مفتاحك العام قبل إرساله إليك عبر الشبكة، وعندما يستقبل عميل ssh الرسالة، تُفكّ شفرتها باستخدام مفتاحك الخاص. وللحصول على ذلك، تحتاج أولًا إلى إنشاء زوج مفاتيح ssh العام والخاص، ثم رفع مفتاحك العام إلى الخادم (وإبقاء مفتاحك الخاص سريًا على جهاز العميل لديك).

ولإنشاء مفتاح ssh، شغّل ssh-keygen -t rsa. وسيطالبك بموقع المفتاح، وهو افتراضيًا في دليلك ~/.ssh، ويطلب منك إدخال عبارة المرور لمفتاح ssh الذي تُنشئه:

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
...

وإذا نجحت العملية، ينشئ ذلك ملفين جديدين في دليلك ~/.ssh:

  • id_rsa: مفتاحك الخاص (لا تشاركه)
  • id_rsa.pub: مفتاحك العام (الذي يمكنك رفعه إلى خواديم الويب)

ويمكنك بعد ذلك نسخ محتويات ملفك id_rsa.pub ولصقها في خادم الويب الذي يتطلبها.

ويمكنك أيضًا استخدام هذه الطريقة لرفع مفتاحك العام إلى مضيفات بعيدة للاتصال باستخدام عبارة مرور مفتاحك بدلًا من كلمة مرورك.

17.3.3. المراجع

لمزيد من المعلومات انظر:

عادةً ما تُثبَّت على أنظمة Unix عدة برامج تحرير مختلفة لإنشاء ملفات النصوص وتحريرها. وتُستخدم ملفات النصوص عادةً لشيفرة البرامج المصدرية وبيانات البرامج والتوثيق. ولا يفسّر Unix لواحق الملفات على أنها تعني شيئًا عن محتوى الملف. غير أن المستخدمين يستخدمون عادةً اصطلاحات تسمية للواحق في كثير من ملفاتهم. فمثلًا، اللاحقة .c هي اصطلاح التسمية لملفات شيفرة C المصدرية (مثل myprog.c)، واللاحقة .h هي اصطلاح التسمية لملفات ترويسة C (مثل stdio.h).

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

ويُعدّ Vim (vi improved) وEmacs مثالين على المحررات المتاحة على معظم الأنظمة، ويكون vim (أو vi) متاحًا دائمًا على كل نظام Unix. ولهذين المحررين واجهة نصية لا واجهة رسومية (وإن كان لكل منهما نسخ بواجهة رسومية أيضًا). وبشكل عام، تتطلب المحررات النصية جهدًا أكبر قليلًا في تعلّم استخدامها مقارنة بالمحررات الرسومية التي تستخدم القوائم وواجهات التأشير والنقر. غير أن المحررات النصية تستهلك عادةً موارد حاسوبية أقل للتشغيل، وهي مفيدة بشكل خاص عند الاتصال عن بُعد بأنظمة تريد تحرير ملفات عليها.

وتوجد موارد على الإنترنت كثيرة لتعلّم Vim وEmacs معًا. وليس مهمًا تعلّم كليهما، وتوجد آراء قوية حول أيّهما أفضل (https://en.wikipedia.org/wiki/Editor_war). غير أنه بما أن Vim (أو vi) متاح على كل نظام Unix، فإننا نعرض تفاصيل أكثر قليلًا عن أساسيات محرر vim وكيفية تعلّم استخدام Vim (وvi). كما نضمّن بعض المعلومات عن أساسيات Emacs وNano، مع مزيد من المراجع للثلاثة جميعًا.

17.4.1. محرر Vim

محرر vim (وvi) هو المحرر الأوسع توفرًا على أنظمة Unix. وهو محرر نصوص فعّال وخفيف وقوي، ويسهل استخدامه بعد تعلّم بعض الأوامر الأساسية التي يمكنك تعلّمها بخوض الدرس التعليمي vimtutor. ويكون vim مفيدًا بشكل خاص عند العمل عن بُعد عبر اتصال ssh. كما أن له ميزات متقدمة كثيرة وهو قابل للتهيئة بدرجة كبيرة عبر إعدادات في ملف التهيئة .vimrc.

وليس ضروريًا تعلّم كل ميزات vim؛ فبعض الأوامر الأساسية ستُطلقك مع vim وتتيح لك أداء معظم مهام التحرير.

ويعمل Vim في نمطين:

  1. نمط الإدراج (insert mode): تُفسَّر ضغطات المفاتيح كإدراجات في محتوى الملف عند موضع المؤشر.
  2. نمط الأوامر أو الهروب (command or escape mode): تُفسَّر ضغطات المفاتيح كأوامر vim تتيح للمستخدم أشياء مثل الحفظ أو الخروج أو البحث أو نقل موضع المؤشر في الملف.

وللتبديل من نمط الإدراج إلى نمط الأوامر، اضغط المفتاح ESC.

وتوجد طرق كثيرة للتبديل من نمط الأوامر إلى نمط الإدراج. وإحدى الطرق ضغط المفتاح i. ومن أفضل طرق تعلّم vim استخدام vimtutor، وهو الدرس التعليمي لـVim.

vimtutor

أفضل طريقة لتعلّم محرر vim هي تشغيل vimtutor:

$  vimtutor

وللبدء في vim، نوصيك بالتركيز على أقسام vimtutor المدرجة أدناه (تغطي الأقسام الأخرى ميزات أكثر غموضًا لا حاجة إليها). ويستغرق خوض جميع هذه الدروس نحو 20 دقيقة.

  • الدرس 1 كاملًا (التنقل، x، i، A، :wq)
  • الدرس 2.6 (dd)
  • الدرس 2.7 (التراجع)
  • الدرس 3.1 (p) والدرس 3.2 (r)
  • الدرس 4.1 (G) والدرس 4.2 (البحث)
  • الدرس 6.2 (a) والدرس 6.3 ( R ) والدرس 6.4 (y وp)

مرجع أوامر Vim

وفيما يلي قائمة بأوامر vim الشائعة. وقد يفيدك الرجوع إليها أثناء تعلّم vim.

  • التبديل بين الأنماط: i التبديل إلى نمط الإدراج: ما تكتبه يظهر في الملف عند المؤشر. ESC التبديل إلى نمط الأوامر: ما تكتبه يُفسَّر كأوامر vim
  • أوامر التنقل: k نقل المؤشر سطرًا إلى الأعلى (يمكن استخدام مفاتيح الأسهم أيضًا). j نقل المؤشر سطرًا إلى الأسفل. l نقل المؤشر محرفًا إلى اليمين. h نقل المؤشر محرفًا إلى اليسار. CTRL-u نقل المؤشر صفحة إلى الأعلى. CTRL-f نقل المؤشر صفحة إلى الأسفل. SHFT-g نقل المؤشر إلى نهاية الملف. : نقل المؤشر إلى بداية السطر رقم (مثل :1 ينقل المؤشر إلى بداية الملف
  • أوامر الحفظ والخروج: :w حفظ التغييرات (كتابة) في الملف. :q الخروج من vim. :q! الخروج دون حفظ التغييرات
  • أوامر التبديل إلى نمط الإدراج: i إدراج: ما تكتبه يظهر عند المؤشر. a إلحاق: ما تكتبه يظهر بعد المؤشر. o فتح: يبدأ سطرًا جديدًا في الملف أسفل المؤشر. O فتح: يبدأ سطرًا جديدًا في الملف أعلى المؤشر
  • أوامر النسخ واللصق والحذف: yy نسخ السطر الحالي (السطر الذي عليه المؤشر). yy نسخ من الأسطر بدءًا من السطر الذي عليه المؤشر (مثل 8yy ينسخ 8 أسطر). p لصق ما نُسخ بعد السطر الذي عليه المؤشر. P لصق ما نُسخ قبل السطر الذي عليه المؤشر. x إزالة المحرف عند المؤشر. x إزالة من المحارف بدءًا من المحرف عند المؤشر (مثل 4x يزيل 4 محارف). dd حذف السطر الحالي (وسيلصقه p عائدًا). dd حذف من الأسطر بدءًا من السطر الذي عليه المؤشر. :u التراجع عن الإجراء، ويمكن التراجع مرات عدة. :redo إعادة الإجراء الذي تُراجع عنه، ويمكن الإعادة بقدر ما تراجعت
  • أوامر البحث والاستبدال: / البحث عن في الملف، فيجد أول ظهور من موضع المؤشر (مثل /hello يبحث عن hello في الملف). n العثور على الظهور التالي لنمط البحث متقدمًا. N العثور على الظهور التالي لنمط البحث متراجعًا. :s// استبدال الظهور التالي لـ بـ. :s///g /g: استبدال كل ظهورات بـ
  • أوامر التحديد المرئي: SHIFT-v تحديد السطر مرئيًا (استخدم مفاتيح الأسهم لتحديد أسطر أكثر). = إعادة تنسيق النص المحدّد مرئيًا (إزاحة تلقائية لشيفرة C!). gq إعادة تنسيق النص المحدّد مرئيًا بحد أقصى 80 محرفًا لطول السطر
  • أوامر متنوعة: :set number إظهار أرقام الأسطر. :set nonumber عدم إظهار أرقام الأسطر. J ضم السطر أسفل المؤشر بالسطر الذي عليه المؤشر. :help توثيق مساعدة عن أمر vim محدّد، و:q للخروج من توثيق المساعدة (مثل help v_) معلومات مساعدة التحديد المرئي. استخدم (: help v_) للتنقل بين مساعدات أوامر التحديد المرئي المختلفة

تهيئة vim

واختياريًا، يمكنك تهيئة vim بكل الطرق. ومع أنك غير ملزم بتهيئة vim لاستخدامه، فقد يفيدك تخصيص نظام الألوان أو ضبط بعض الإعدادات الأخرى.

أضف في دليلك الرئيسي ملف تهيئة لـvim اسمه .vimrc. وعند البدء، يفحص vim هذا الملف لضبط خيارات تهيئة مختلفة لجلسة vim، بما فيها ضبط نظام ألوان وحجم نافذة افتراضي.

شغّل ls -a في دليلك الرئيسي لسرد جميع ملفات النقطة لديك ولمعرفة ما إذا كان ملف .vimrc موجودًا بالفعل:

$ cd
$ ls -a ~/       # ~/ is shorthand for /home/you/

وإذا كان ملف .vimrc موجودًا، يمكنك فتحه في vim وتحريره كأي ملف آخر:

$ vim  .vimrc

وإذا لم يكن موجودًا، يمكنك إنشاء .vimrc في دليلك الرئيسي ونسخ ملف .vimrc نموذجي موجود لصديق أو تجده على الإنترنت إليه. فمثلًا:

$ pwd
/home/sarita/
$ vim  .vimrc

وتوجد موارد على الإنترنت كثيرة بمعلومات عن تهيئة إعدادات vim.

17.4.2. محرر Emacs

Emacs محرر آخر متاح دائمًا تقريبًا على أنظمة Unix. وليس ضروريًا تعلّم واستخدام كلٍّ من Vim وEmacs، لكن من المفيد معرفة أحدهما، وربما معرفة أساسيات قليلة عن الآخر (على الأقل كيفية الخروج من كل منهما).

وخلافًا لـVim، فإن Emacs محرر أحادي النمط، أي أنه يعمل في نمط إدراج واحد تُصدر فيه الأوامر، مثل حفظ الملف، عبر تركيبات مفاتيح خاصة مع المفاتيح CTRL وALT وSHIFT. والمقايضة الرئيسية بين Vim وEmacs أن لـEmacs تسلسلات مفاتيح أكثر تعقيدًا من Vim للأوامر، لكنه لا يملك النمطين الذين يحتاج المستخدم إلى التبديل بينهما كما في Vim. وEmacs مثل سكين الجيش السويسري في أنه يملك تشكيلة غنية من وحدات الماكرو والأوامر التي يمكن استخدامها لمجموعة واسعة من المهام (بما فيها ميزات لا تُستخدم شائعةً). ولهذا السبب، لا يُثبَّت Emacs عادةً على الأنظمة محدودة الذاكرة لأنه يتطلب ذاكرة أكثر من تطبيق مثل Vim عادةً.

ولبدء emacs، شغّله من سطر الأوامر على الملف الذي تريد تحريره. فمثلًا، لتحرير myprog.c في emacs:

$ emacs myprog.c

وعندما يكتب المستخدم ضغطات مفاتيح عادية، تُضاف المحارف المُدخَلة إلى الملف بعد المؤشر.

مرجع أوامر Emacs

وفيما يلي قائمة بأوامر Emacs الشائعة. وقد يفيدك الرجوع إليها أثناء تعلّم Emacs.

  • التحكم في الملفات (وهي تتضمن تسلسلات متعددة): CTRL-x CTRL-s أي استمر في الضغط على المفتاح CTRL واكتب x ثم s. CTRL-x CTRL-s حفظ تعديلات الملف. CTRL-x CTRL-c الخروج من Emacs
  • تحريك المؤشر: CTRL-n تحريك المؤشر سطرًا إلى الأمام. CTRL-p تحريك المؤشر سطرًا إلى الخلف. CTRL-b تحريك المؤشر محرفًا إلى الخلف. CTRL-f تحريك المؤشر محرفًا إلى الأمام
  • حذف المحتوى: CTRL-d يحذف محرفًا. CTRL-k يحذف سطرًا.
  • نسخ المحتوى: CTRL-k نسخ سطر. CTRL-y لصق
  • البحث: CTRL-s البحث عن نمط
  • أوامر متنوعة: CTRL-g الخروج من تسلسل أوامر emacs (مفيد إذا كتبت شيئًا خاطئًا). CTRL-x-0 إغلاق نافذة المخزن الحالية. CTRL-x-1 إغلاق كل المخازن الأخرى عدا الحالي. CTRL-x-2 فتح نافذة مخزن أسفل. CTRL-x-3 فتح نافذة مخزن إلى اليمين. CTRL-x-o التبديل إلى مخزن آخر. ESC-x الدخول إلى النمط الفوقي (في توثيق Emacs وفي هذا الجدول لاحقًا يُرمز إليه بـM-x). M-x goto-line n الانتقال إلى السطر n في الملف. M-x linum-mode تشغيل ترقيم الأسطر. M-x ansi-term فتح صدفة bash في المخزن الحالي

ومثل Vim، يملك Emacs مجموعة غنية من الأوامر التي تدعم مهام التحرير المعقّدة. وهو أيضًا قابل للتهيئة عبر ملف تهيئة، ~/.emacsrc، يمكنك فيه تخصيص وظائف emacs ومظهره.

17.4.3. محرر Nano

Nano محرر نصوص آخر مفيد لإجراء تعديلات أصغر على الملفات---فهو سهل الاستخدام، لكنه يفتقر إلى بعض ميزات Emacs وVim التي تجعلهما مفيدين لمهام تحرير أكبر وأكثر تعقيدًا. كما أن Nano محرر أحادي النمط مثل Emacs يستخدم تركيبات مفاتيح خاصة لأوامر مثل تحريك المؤشر أو حفظ الملف.

ويُشغَّل Nano على نحو مشابه لـVim وEmacs؛ لفتح ملف للتحرير اكتب nano والملف المراد تحريره على سطر الأوامر:

$ nano myprog.c

وعند تشغيلك nano، يسرد بعض أوامره الأكثر شيوعًا في الأسفل. وفي سرده، تعني M- عادةً مفتاح ALT (الفوقي) زائد مفتاح آخر، وC- هو المفتاح CTRL. وبشكل عام، تسلسلات التحكم بالأوامر في nano أبسط من نظيراتها في emacs. فمثلًا، يُستخدم CTRL-x للخروج من nano.

17.4.4. ضبط المحرر الافتراضي

برنامج المحرر الافتراضي لديك هو الذي تفتحه تلقائيًا برامج أخرى تبدأ جلسة تحرير لبعض وظائفها. فمثلًا، تبدأ برمجيات التحكم في الإصدارات مثل Git محررًا لإضافة تعليقات إلى إيداع (commit). ويمكنك رؤية محرر النصوص الافتراضي لديك على أنظمة Linux بطباعة متغيري البيئة EDITOR وVISUAL:

$ echo $EDITOR
nano
$ echo $VISUAL
nano

ولضبط محررك الافتراضي، أضف تعريفات هذين المتغيرين البيئيين أو عدّلها في ملف .bashrc لديك، ثم شغّل source ~/.bashrc لرؤية التغييرات. وهذا مثال على ضبط كليهما على vim:

$ vim ~/.bashrc

  export EDITOR=vim
  export VISUAL=vim

$ source ~/.bashrc
$ echo $EDITOR
vim
$ echo $VISUAL
vim

وفي قسم ملفات النقطة نتحدث عن متغيرات البيئة و.bashrc بمزيد من التفصيل.

17.4.5. المراجع

توجد موارد كثيرة على الويب لتعلّم Vim أو Emacs أو Nano، منها عدة مقاطع فيديو تعليمية. وهذه مجرد بعض الروابط إلى مراجع ودروس تعليمية:

نناقش في هذا القسم الأمر make وملفات Makefile. ونعرض بعض الأمثلة بدءًا من ملف Makefile بسيط وصولًا إلى ملف Makefile أكثر تعقيدًا وأكثر عمومية (القسم 17.5.10) يُعدّ نقطة بداية جيدة لمعظم الاستخدامات. ولمزيد من المعلومات عن كتابة ملفات Makefile انظر دليل GNU Make.

17.5.1. عن make

تُبسّط أداة Unix المسمّاة make ترجمة البرامج. ويكتب المستخدم (أو برنامج) ملف Makefile بالقواعد المراد تنفيذها. ويشغّل المستخدم make فحسب، فيقرأ برنامج make القواعد من ملف Makefile التي تخبره كيف يترجم برنامجًا.

ومن فوائد make أنه يستطيع تنفيذ أوامر معقّدة لبناء الملفات التنفيذية بمجرد كتابة make بدلًا من اضطرار المستخدم إلى كتابة أوامر gcc طويلة للبناء في كل مرة. وغالبًا ما تُقسَّم البرامج الأكبر إلى وحدات منفصلة تتكوّن من ملفات .c و.h متعددة. وقد تربط أيضًا مكتبات متعددة، أو تخزّن ملفات الترويسة والمكتبات في مواقع غير قياسية. وكل هذا يجعل الترجمة أكثر تعقيدًا، وأسطر أوامر gcc أطول وأكثر تعقيدًا. وفي هذه الحالات، تكون كتابة سطر أمر gcc الكامل يدويًا عند مطالبة الصدفة مستهلكة للوقت وعرضة للخطأ معًا. فمثلًا، هذا سطر أمر gcc طويل سيكون مملًا لكتابته يدويًا:

gcc -Wall -g  -I../includes -I. -o mycc emitter.c init.c lexer.c symbol.c parser.c ast.c error.c symbol_table.c codegen.c emitcode.c main.c -L../libs -lmylib -lpthread

وباستخدام make، يكتب المستخدم make ببساطة لتنفيذ الأوامر المدرجة في ملف Makefile.

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

ويكتب المستخدم عادةً ملف Makefile بالقواعد التي ينفّذها make. وتوجد أيضًا برامج، مثل CMake وGNU Autotools، تولّد ملفات Makefile. وتُستخدم هذه عادةً لبناء البرمجيات الكبيرة ولدعم الترجمة بسهولة عبر أنظمة مختلفة، بما في ذلك العثور على المترجمات والبرمجيات الأخرى والمكتبات وملفات الترويسة اللازمة على نظام معيّن.

17.5.2. استخدام make

وخطوات استخدام make هي:

أنشئ ملف Makefile يسرد قواعد بناء الملف التنفيذي. وينبغي أن يُسمّى الملف افتراضيًا 'Makefile'. ولا يلزم فعل ذلك إلا مرة واحدة، إلا عند إضافة وحدات جديدة (ملفات .c أو .h) إلى البرنامج، فحينئذٍ يجب تحديث ملف Makefile لإضافة تبعيات الوحدات الجديدة إلى القواعد القائمة أو لإضافة قواعد جديدة تشملها في الترجمة.

بعد تحرير ملف/ملفات البرنامج، أعد بناء الملف التنفيذي بكتابة make:

$ make

ويمكن تنفيذ قاعدة محدّدة في ملف Makefile بكتابة:

$ make target_label

فمثلًا، لتنفيذ الأمر rm لإزالة جميع الملفات المبنية في ملف Makefile المثال المدرج أدناه، اكتب:

$ make clean

17.5.3. إنشاء Makefile

يبدأ ملف Makefile عادةً ببعض تعريفات المتغيرات التي تتبعها مجموعة من القواعد (أو مدخلات الأهداف) لبناء أهداف محدّدة (عادةً لبناء ملفات ‎.o والملفات التنفيذية). وتتكوّن كل قاعدة من وسم هدف ومجموعة من الأوامر المرتبطة به (تُسمى أحيانًا وصفات recipes) التي تُشغَّل للهدف. وفيما يلي الصيغة العامة للقاعدة:

# comment
# (note: the <tab> in the command line is necessary for make to work)
target:  dependency1 dependency2 ...
      <tab> command

وهذا مثال:

# target entry to build program executable from program.o
# and util.o object files
#
program: program.o util.o
	gcc -o program program.o util.o

تحذير — make ومحارف المسافات البيضاء

make دقيق جدًا بشأن محارف المسافات البيضاء. فمثلًا، علامة في السطر الذي يسبق جزء الأمر في مدخل هدف Makefile مهمة جدًا ليفسّر make مدخل الهدف تفسيرًا صحيحًا وينفّذ جزء الأمر. فإذا استخدمت مثلًا محارف مسافة بدلًا من ، فلن يفسّر make مدخل الهدف تفسيرًا صحيحًا ولن يشغّل أمره المرتبط.

17.5.4. ملف Makefile بسيط

وهذا مثال على ملف Makefile بسيط يبني ملفًا تنفيذيًا من ملف .c واحد اسمه myprog.c:

# build an executable named myprog from myprog.c
all: myprog.c
	gcc -g -Wall -o myprog myprog.c

clean:
	rm myprog

تؤدي كتابة make إلى تشغيل الأمر المرتبط بوسم الهدف all لتنفيذ gcc -g -Wall -o myprog myprog.c إذا كان تاريخ تعديل الملف myprog.c أحدث من تاريخ تعديل الملف myprog أو إذا لم يكن myprog موجودًا. لاحظ أن الاسم all ليس خاصًا، فـmake يختار افتراضيًا أول وسم هدف في ملف Makefile لتنفيذه. ولتنفيذ قاعدة محدّدة غير الأولى المدرجة في ملف Makefile، اسرد اسم الهدف على سطر أمر make. فمثلًا، يشغّل make clean الأمر rm myprog لإزالة الملف التنفيذي المبني سابقًا myprog.

وفي القسم 17.5.8 نوضّح كيفية استخدام متغيرات Makefile لكتابة نسخ أكثر عمومية من ملف Makefile هذا---نسخة يمكن نسخها وتعديلها تعديلًا بسيطًا لإنشاء ملف Makefile جديد لبناء برنامج مختلف.

17.5.5. ملف Makefile لملفات مصدر متعددة

وفيما يلي مثال على ملف Makefile لبناء ملف تنفيذي من عدة ملفات .c و.h. وهو يترجم أولًا كل .c إلى ملف .o، ثم يربط ملفات .o والمكتبات معًا لبناء الملف التنفيذي. ولأنه يسرد التبعيات لكل ملف .o، فإذا عُدّل ملف .c واحد فلن يلزم إعادة بناء سوى ملف .o واحد (الذي يعتمد على ملف .c المعدّل)، ولا يعيد بناء جميع ملفات .o. وهذه ميزة جيدة بشكل خاص تسرّع زمن الترجمة للبرامج الأكبر المكوّنة من ملفات .c كثيرة.

# Example Makefile for a program with many .c modules
# make: build count executable program
# make clean: clean-up all built files

all: count

# To create the executable file count we need the object files
# countwords.o, counter.o, and scanner.o:
count: countwords.o counter.o scanner.o
	gcc -g -Wall -o count countwords.o counter.o scanner.o

# To create the object file countwords.o, we need the source
# files countwords.c, scanner.h, and counter.h:
countwords.o: countwords.c scanner.h counter.h
	gcc -g -Wall -c countwords.c

# To create the object file counter.o, we need the source files
# counter.c and counter.h:
counter.o: counter.c counter.h
	gcc -g -Wall -c counter.c

# To create the object file scanner.o, we need the source files
# scanner.c and scanner.h:
scanner.o: scanner.c scanner.h
	gcc -g -Wall -c scanner.c

# To start over from scratch, type 'make clean'.  This
# removes the executable file, as well as old .o object
# files and *~ backup files:
clean:
	rm -f count *.o *~

وبعد تشغيل make المرة الأولى، إذا عُدّل ملف counter.c، فلن تُنفَّذ في تشغيل make التالي سوى هاتين القاعدتين (بسبب تبعيتهما على counter.c أو counter.o):

counter.o: counter.c counter.h
	gcc -g -Wall -c counter.c

count: countwords.o counter.o scanner.o
	gcc -g -Wall -o count countwords.o counter.o scanner.o

وفي القسم 17.5.9 والقسم 17.5.10 نوضّح كيفية استخدام متغيرات Makefile وقواعد Makefile الضمنية وتوليد التبعيات التلقائي لكتابة نسخ أكثر عمومية من ملف Makefile. وملف Makefile في القسم 17.5.10 ملف عام جيد للتطبيقات المبنية من ملفات .c و.h كثيرة، والتي قد تُبنى بملفات ترويسة أو مكتبات مخزّنة في مواقع غير قياسية.

17.5.6. أخطاء make

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

وستُبلَّغ أخطاء صياغة Makefile مع رقم السطر في ملف Makefile الذي وقع فيه الخطأ لمساعدتك على تتبّع مشكلة الصياغة. غير أن هناك بعض الأخطاء الشائعة التي يصعب رؤيتها لأنها متعلقة بأخطاء في استخدام محارف المسافات البيضاء. ومن أمثلة هذا النوع مثالان:

استخدام محارف مسافة بدلًا من في بداية جزء الأمر في مدخل هدف. فمثلًا، إذا كانت هناك محارف مسافة في بداية السطر الثاني من مدخل الهدف هذا بدلًا من محرف، فلا يستطيع make تفسير هذه القاعدة تفسيرًا صحيحًا:

all: myprog.c
     $(CC) $(CFLAGS) $(INCLUDES) -o myprog myprog.c

وجود مسافات زائدة في نهاية بادئة اسم ملف يوسّعه ملف Makefile في مواضع أخرى بإضافة لواحق مختلفة إليه. فمثلًا:

TARGET = myprog
SRC = $(TARGET).c

فإذا كانت هناك مسافة بيضاء زائدة بعد myprog، فبدلًا من أن يكون SRC هو myprog.c سيكون شيئًا مثل myprog .c، وهو اسم ملف غير صالح.

وإذا كان الخطأ في الترجمة أو الربط، فقد يكون بسبب أخطاء في ملف Makefile أو بسبب خطأ في برنامجك. ومن الأمثلة على أخطاء Makefile التي تسبب أخطاء ترجمة أو ربط: قاعدة مفقودة لترجمة ملف .o؛ أو تبعيات مفقودة لا تفرض إعادة الترجمة؛ أو ربط صريح مفقود لمكتبة لازمة؛ أو مسارات تضمين مفقودة (-I أو -L) لمواقع ملفات الترويسة والمكتبات اللازمة لترجمة البرنامج. وكثير من هذه الأخطاء سيظهر كأخطاء gcc، لكنها تتطلب إصلاحات في ملف Makefile لا في شيفرة البرنامج.

ومن النصائح التي تساعدك على تحديد ما إذا كان يجب إصلاح ملف Makefile أو الشيفرة المصدرية أو كليهما:

  • كخطوة أولى لمعرفة ما إذا كانت لديك تبعية مفقودة، شغّل make clean ثم make لإعادة بناء كل شيء من الصفر. فإذا نجحت العملية، فهناك تبعية مفقودة في ملف Makefile.
  • إذا لم تجد ملف .o تحتاج إلى ربطه، فالمشكلة أنك تحتاج إلى إضافة قاعدة لبنائه (وربما تحديث تعريف SRCS ليشمل هذا الملف الجديد).
  • إذا وقع خطأ بأن المترجم لا يستطيع العثور على ملف .h، فهذا يدل على أنك تحتاج إلى تحديث متغير INCLUDES بإضافة `` بالصيغة -I[path to include directory] لإخبار make بمكان العثور عليها.
  • وبالمثل، إذا وقعت أخطاء بأن المترجم لا يستطيع العثور على المكتبات (وهي تظهر كأخطاء غير معرّفة أثناء مرحلة الترجمة النهائية، أي الربط)، فتحتاج إلى تحديث تعريفات LIBDIR أو LIBS لتشمل مسارات المكتبات (-L) أو إلى إضافة أي مكتبات مفقودة تحتاج إلى الربط (-l) إلى سطر الأوامر.
  • وفي بعض الحالات التي يكون فيها الخطأ في ملف مصدري بلغة C، مثل #include مفقودة، قد يلزم تحديث ملف المصدر C وملف Makefile معًا لإصلاح الخطأ. فمثلًا، إضافة #include إلى ملف مصدري بلغة C قد تتطلب أيضًا تحديث مسار التضمين (-I)، وفي بعض الحالات أيضًا مسار مكتبة (-L) وربطًا صريحًا لمكتبة (-l) في ملف Makefile.

17.5.7. make المتوازي

بالنسبة للبرامج الكبيرة المكوّنة من ملفات .c و.h كثيرة، قد تستغرق الترجمة وقتًا طويلًا. وقد يكون كثير من الوقت ناتجًا عن ترجمة ملف .o من كل ملف .c. وترجمة كل .o مستقلة عن ترجمة ملفات .o الأخرى، لذا يُعدّ ذلك هدفًا جيدًا للتوازي حيث يمكن لخيوط أو عمليات متعددة أن تترجم في الوقت نفسه ملف .c مختلفًا بالتوازي. واستخدام خيار سطر الأوامر -j[num] مع make يخبره بتشغيل ما يصل إلى num من المهام في المرة، أو تنفيذ num من الوصفات المختلفة بالتوازي في الوقت نفسه.

فمثلًا، لجعل make متوازيًا بحيث ينفّذ 12 وصفة في المرة اكتب:

make -j12

وعلى جهاز فيه 12 نواة أو أكثر، سيعمل هذا أسرع بنحو اثنتي عشرة مرة من make.

17.5.8. متقدم: ملف Makefile بسيط عام

يمكننا إعادة كتابة ملف Makefile البسيط من القسم 17.5.4 لجعله أكثر عمومية. وملف Makefile العام هو ملف يسهل تحريره لاستخدامه في برنامج آخر (فالمستخدمون عادةً يبدؤون ملف Makefile جديدًا بنسخة من ملف موجود ثم يعدّلونها، بدلًا من كتابة ملف من الصفر في كل مرة). ويستبدل ملف Makefile الأكثر عمومية هذا أسماء ملفات التطبيق المحدّدة في القواعد بمتغيرات Makefile، فيصبح القاعدة عامة لأي برنامج. وصياغة تعريف متغيرات Makefile هي =. فمثلًا:

# define CC to be the name of the compiler
CC = gcc

وبعد تعريفه، يمكن استخدام متغير Makefile لاحقًا في ملف Makefile بصيغة $(variable_name). فمثلًا:

# use the value of CC in a target entry:
all:
    $(CC) -o myprog myprog.c

وفيما يلي مثال على نسخة أكثر عمومية قليلًا من ملف Makefile البسيط أعلاه لبناء ملف تنفيذي من ملف .c واحد. وتحدّد هذه النسخة الأكثر عمومية من ملف Makefile البسيط متغيرات تشير إلى المترجم المستخدم (CC)، ولواحق المترجم المراد تمريرها (CFLAGS)، واسم ملف المصدر .c (SRCS)، واسم الملف التنفيذي للبرنامج المراد بناؤه (TARGET). ومتغير RM متغير Makefile مدمج معرّف ليكون الأمر rm. ويمكنك تغيير قيمة أي متغير مدمج بإعادة تعريفه ليكون شيئًا آخر في ملف Makefile.

وباستخدام متغيرات Makefile، تكون النتيجة أن كل شيء في ملف Makefile بدءًا من all: عام وسيعمل مع أي برنامج. ولا يلزم تغيير سوى المتغيرين SRCS وTARGET لبناء برنامج مختلف.

# define the compiler: gcc for C programs
CC = gcc

# define the compiler flags:
#  -g    adds debugging information to the executable file
#  -Wall turns on most, but not all, compiler warnings
CFLAGS  = -g -Wall

# source file (could actually list multiple .c files here)
SRCS = myprog.c

# define the executable file (assume source is same name):
TARGET = myprog

# this is generic:
all: $(TARGET)

$(TARGET): $(SRCS)
	$(CC) $(CFLAGS) -o $(TARGET) $(SRCS)

clean:
	$(RM) $(TARGET)

ويمكن استخدام ملف Makefile هذا أيضًا لبناء ملف تنفيذي من أكثر من ملف مصدر .c واحد بمجرد تعريف SRCS ليسرد كل ملف c. فمثلًا، إذا بُني الملف التنفيذي من ملفي مصدر myprog.c وutil.c، فعرّف SRCS ليكون:

SRCS = myprog.c util.c

ومن الأمور الجديرة بالملاحظة في ملف Makefile هذا أنه يعيد دائمًا ترجمة الملف التنفيذي من جميع ملفات المصدر المدرجة في SRCS إذا عُدّل أي منها (بسبب هذه التبعية: $(TARGET): $(SRCS)). وبالنسبة للبرامج الصغيرة، التي قد تتكوّن من ملف .c واحد أو بضعة ملفات فقط، قد يكون ذلك مقبولًا. غير أننا نستطيع تعديل ملف Makefile ليعيد بناء الأجزاء التي تحتاج إلى إعادة بناء فقط عند تعديل ملف مصدر. ونفعل ذلك بإضافة قواعد لترجمة ملفات الكائنات (.o) من كل ملف .c، ثم لبناء الملف التنفيذي بربط ملفات .o (وأي مكتبات) في الملف التنفيذي.

17.5.9. متقدم: ملف Makefile عام لملفات متعددة

يمكننا إعادة كتابة ملف Makefile من القسم 17.5.5 الذي ترجم ملفًا تنفيذيًا من ملفات مصدر متعددة كملف Makefile أكثر عمومية بالاستفادة من متغيرات Makefile وقواعد استبدال الأنماط.

لاحظ أن قواعد بناء ملفات .o من .c في ملف Makefile الأصلي (في القسم 17.5.5) متشابهة كلها عدا أسماء الملفات المحدّدة. وسيكون جميلًا لو أمكن تبسيط ذلك بقاعدة أكثر عمومية لبناء .o من ملفات .c.

وإحدى طرق فعل ذلك استخدام قاعدة استبدال نمط تُفعَّل لأي ملف .o يُبنى من ملف .c. وفيما يلي مثال على قاعدة كهذه. وهي تستخدم المتغيرين التلقائيين في Makefile: $< لاسم تبعية القاعدة (ملف مصدر .c) و$@ للملف الهدف (ملف الكائن .o). انظر دليل gnu make لمزيد من المعلومات عن المتغيرات التلقائية.

# This is a pattern replacement rule for building .o files from their
# associated .c files.  It uses automatic variables:
#   $<  the name of the first dependency of the rule (a .c file)
#   $@  the name of the target of the rule (a .o file)
%.o : %.c
        $(CC) $(CFLAGS) -c $< -o $@

وهذه قاعدة أقصر بكثير، وقاعدة عامة، ستعمل مع أي ملف Makefile للغة C.

غير أننا نستطيع فعل ما هو أفضل من ذلك! فبما أن ترجمة .o من ملف .c نمط بناء شائع جدًا، فإن make يملك قاعدة ضمنية لبناء ملفات .o من ملفات .c. وإذا أردنا استخدام هذه القاعدة الضمنية، فلا نضيف أي قاعدة لبناء ملفات .o من ملفات .c إلى ملف makefile لدينا. والقاعدة الضمنية هي التالية:

# make's implicit rule for building .o files from .c:
%.o : %.c
	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

وتستخدم القاعدة الضمنية بعض متغيرات makefile التي لها قيم افتراضية، لكن يمكن أيضًا إعادة تعريفها في ملف Makefile (وهو ما يحدث عادةً):

  • CC: يعرّف المترجم (يعرّف ملف Makefile لدينا CC ليكون gcc)
  • CFLAGS: يعرّف لواحق المترجم (يعرّف ملف Makefile لدينا هذه اللواحق لتكون -g -Wall، وهي تضيف معلومات التصحيح وتشغّل جميع التحذيرات).
  • CPPFLAGS: يعرّف لواحق المعالج الأولي (preprocessor) (ولا يعرّف ملف Makefile لدينا أيًّا منها). والمعالج الأولي هو الجزء الأول من المترجم الذي يعمل. وهو يوسّع أي شيء يبدأ بـ#، مثل #include. وإليك مزيدًا من المعلومات عن مراحل الترجمة.

ولأن لدينا قاعدة لبناء $(TARGET) لها تبعية $(OBJS)، فسيبحث make عن قاعدة لبناء ملفات الكائنات (.o) من ملفات المصدر (ملفات .c)، فإن لم يجد واحدة في ملف Makefile استخدم قاعدته الضمنية. وإذا كفت القاعدة الضمنية، فلا حاجة إلى تضمين أي قاعدة لبناء ملفات الكائنات في ملف Makefile لدينا! بل نعرّف فقط متغيرات makefile التي تستخدمها (أو نستخدم تعريفاتها الافتراضية). وإذا لم تعجبنا القاعدة الضمنية، فيجب أن نتجاوزها بقاعدتنا الخاصة.

وفيما يلي نسخة من ملف Makefile تستخدم القاعدة الضمنية لبناء ملفات .o (ولاحظ أن كل شيء في ملف Makefile بدءًا من OBJS = عام!):

# Example Makefile using make's implicit .c:.o rule
#   make: build count executable program
#   make clean: clean-up all built files

# define the compiler
CC = gcc

# define compiler flags
CFLAGS  = -g -Wall

# define the executable file
TARGET = count

# define source files
SRCS = countwords.c counter.c scanner.c

# define the object files
# This uses Suffix Replacement within a macro:
#   $(name:str1=str2): for each word in 'name' replace 'str1' with 'str2'
# Here replace the suffix .c of all words in the macro SRCS with .o suffix
#
OBJS = $(SRCS:.c=.o)

# typing 'make' will invoke the first target entry in the file
all: $(TARGET)

# To create the executable file count we need the object files
# countwords.o, counter.o, and scanner.o:
#
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)

# using implicit rule to build .o files from .c!

# clean up built files
clean:
	$(RM) -f $(TARGET) *.o *~

وهذا ملف Makefile قصير وعام. غير أن فيه مشكلة خفية واحدة لا نواجهها في النسخة الأطول الأقل عمومية: فقد فقدنا تبعيات ملفات الترويسة على ملفات الكائنات، أي أن المستخدم إذا حدّث ملف ترويسة وكتب make فلن يُعاد بناء الملف التنفيذي وملفات .o، لكننا نريد إعادة بنائها. ولإصلاح هذه المشكلة، يمكننا إضافة تبعيات إضافية إلى ملف Makefile لـTARGET وOBJS أو لملفات كائنات فردية:

# define a variable to be the set of header files
HDRS = scanner.h counter.h

# add a dependency to this rule:
$(TARGET): $(OBJS)  $(HDRS)

# add a generic dependency on all $(OBJS)
# (note: no command part to this rule, it is just defining a dependency):
$(OBJS): $(HDRS) $(SRCS)

ويبدو ملف makefile الناتج كالتالي:

# Example Makefile with implicit .c:.o rule and header file dependencies
# make: build count executable program
# make clean: clean-up all built files

# define the compiler
CC = gcc

# define compiler flags
CFLAGS  = -g -Wall

# define source files
SRCS = countwords.c counter.c scanner.c

# define the object files
# This uses Suffix Replacement within a macro:
#   $(name:str1=str2): for each word in 'name' replace 'str1' with 'str2'
# Here replace the suffix .c of all words in the macro SRCS with .o suffix
#
OBJS = $(SRCS:.c=.o)

# define the header files
HDRS = scanner.h counter.h

# define the executable file
TARGET = count

# typing 'make' will invoke the first target entry in the file
all: $(TARGET)

# To create the executable file count we need the object files
# countwords.o, counter.o, and scanner.o:
#
$(TARGET): $(OBJS) $(HDRS)
	$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)

$(OBJS): $(HDRS) $(SRCS)

# clean up built files
clean:
	$(RM) -f $(TARGET) *.o *~

وبإضافة تبعيات ملفات .h، ستؤدي التغييرات في أي ملف .c أو .h إلى إعادة بناء ملفات .o والملفات التنفيذية. غير أن في هذا الحل خللًا خفيًا واحدًا. فالمشكلة في التبعية العامة للكائنات على جميع المصادر والترويسات، ما يعني أن بعض ملفات .o ستُعاد بناؤها بلا داعٍ عند تحديث ملف .h أو .c معيّن. فمثلًا، سيُعاد بناء scanner.o إذا حُدّث counter.h، لكنه لا يحتاج إلى ذلك. والحل هو تعداد التبعيات المحدّدة لكل ملف .o في ملف Makefile تعدادًا أكثر صراحةً، فتصبح التبعيات أقل عمومية وأكثر تخصّصًا لكل ملف .o. فمثلًا، استبدل $(OBJS): $(HDRS) $(SRCS) في ملف Makefile أعلاه بـ:

count.o: count.c count.h
scanner.o: scanner.c scanner.h
countwords.o: countwords.c scanner.h counter.h

وإضافة هذه التبعيات المحدّدة تجعل ملف Makefile أقل عمومية، لكنها تتجنّب الترجمة غير الضرورية التي يسببها الحل الأكثر عمومية $(OBJS): $(HDRS) $(SRCS). وبالنسبة للبرامج الصغيرة قد يكون الحل الأكثر عمومية مقبولًا، لكن بالنسبة للبرامج الأكبر فإن تعداد التبعيات بعناية لكل ملف .o مهم لتجنّب كثير من إعادة الترجمة غير الضرورية، وكذلك لضمان إعادة بناء ملف .o عند تعديل ملف يعتمد عليه.

ولهذا السبب، توجد طرق لتوليد مجموعة كاملة من التبعيات تلقائيًا لكل ملف .o (ولكل ملف تنفيذي). ويكاد يكون استخدام أدوات توليد التبعيات التلقائي ضروريًا لملفات Makefile الخاصة ببناء برامج من ملفات مصدر وترويسة كثيرة، لضمان الترجمة الصحيحة عند تعديل ملف. ومن هذه الأدوات makedepend، الذي يفحص ملفات .c و.h ويضيف التبعيات إلى نهاية ملف Makefile. وهناك طريقة أخرى هي استخدام راية المعالج الأولي -MD (توليد التبعيات) في gcc لتوليد ملفات التبعية (.d) وتضمين هذه الملفات في ملف Makefile.

17.5.10. متقدم: ملف Makefile عام غني بالميزات

آخر مثال على ملف makefile لدينا هو ملف makefile عام متعدد الأغراض يتضمّن مجموعة أكثر اكتمالًا من متغيرات Makefile الشائعة الاستخدام لترجمة برامج C، كما يوضّح بعض الصياغة والميزات الأكثر تقدمًا، وتحديدًا:

  • الربط بالمكتبات (-l)
  • تعريف مسارات الربط والتضمين (-L و-I) للعثور على ملفات المكتبات والترويسة غير الموجودة في المواقع الافتراضية.
  • التوليد التلقائي لقواعد تبعيات الملفات (-MD) وتضمينها في ملف makefile (include).

وهذا أيضًا مثال جيد على ملف Makefile لنسخه وتحريره لاستخدامك الخاص

وبشكل عام، تُربط مكتبات كثيرة ببرامج C ربطًا ضمنيًا، لكن بعض المكتبات الأخرى تحتاج أحيانًا إلى ربط صريح باستخدام راية المترجم -l. ويبحث مترجم gcc في المواقع الافتراضية عن شيفرة المكتبات، لكن بالنسبة للمكتبات غير الموجودة في تلك المواقع، يجب تضمين مسار ربط صراحةً في سطر أمر gcc. وتُضاف هذه إلى ملف makefile باستخدام -L. وبالمثل، قد تقع ملفات الترويسة أيضًا في مواقع غير قياسية، وحينئذٍ يحتاج ملف Makefile إلى تحديد تعريف مسار ملفات التضمين للموقع الذي يحتويها. وتُعطى مسارات التضمين باستخدام -I.

وأخيرًا، بالنسبة لبرنامج فيه ملفات .c و.h متعددة، من المفيد توليد التبعيات تلقائيًا. وفي هذا المثال نستخدم راية المعالج الأولي -MD (توليد التبعيات) في gcc (المضافة إلى تعريف CPPFLAGS). وهي تنشئ ملفات تبعية (.d) مرتبطة بكل ملف .o أثناء مرحلة المعالجة الأولية للترجمة (المرحلة الأولى). ثم تُضمَّن ملفات التبعية هذه في ملف Makefile (include). وتوجد تبعية ضمنية لملفات .d على ملفات .c، فإذا عُدّل ملف .c أُعيد توليد ملفي .o و.d الخاصين به. كما تُستخدم الراية -MP عادةً مع الراية -MD، وهي تكبح الأخطاء إذا أُعيدت تسمية ملفات الترويسة.

وإليك ملف Makefile الناتج، وهو ملف makefile عام جيد لنسخه وتعديله لاستخدامك الخاص:

# Example Makefile to copy and edit for your own use
#   make: build .d, .o, and executable target
#   make clean: remove built files

# define compiler
CC = gcc

# define compiler flags
CFLAGS = -Wall -g

# define include paths:
#  define any non-default paths to header files (.h) used by this program
#  (default location, /usr/include/, is searched by gcc last)
INCLUDES =  -I../includes -I.

# define preprocessor flags
# -MD and -MP flags create dependency files (.d) for all SRCS
CPPFLAGS = -MD -MP $(INCLUDES)

# library paths
#  define any non-default paths to where libraries used by the program may be
#  (default location, /usr/lib/, is searched by gcc last)
LFLAGS = -L../lib -L/home/newhall/lib

# define libraries to link into executable
#    -lm: std math library in /usr/lib (named libm.so or libm.a)
#    -lmylib: is a library not in standard location, so -L paths
#             are used to find it (named libmylib.so or libmylib.a)
LIBS = -lm -lmylib

# set of source files
#  (\ is the line continuation character)
SRCS =  emitter.c init.c lexer.c  symbol.c parser.c ast.c  \
        error.c symbol_table.c codegen.c emitcode.c main.c

# set of object files
#  obtained from SRCS using suffix replacement rule
OBJS = $(SRCS:.c=.o)

# executable file name
TARGET = mycc

# .PHONY tells make that these targets are not file names but
#   the target labels for a set of commands to run when `make [target label]`
#   is invoked (e.g., `make clean` executes a `rm` command)
#
.PHONY: clean

# the rest of this is generic and should not need to be edited:
all:    $(TARGET)

$(TARGET):  $(OBJS)
	$(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -o $(TARGET) $(OBJS) $(LFLAGS) $(LIBS)

# just use make's built-in rule (.c.o:) to build .o from .c files!

clean:
        $(RM) *.o *~ $(TARGET) *.d

# include the dependency files here
#   this uses a suffix replacement within a macro $(name:string1=string2):
#     (for each word in 'name' replace 'string1' with 'string2')
#     here we replace the suffix .c with .d for every name in SRCS
#
#   the '-' before 'include' suppresses error messages when a .d file
#     doesn't already exist
-include $(SRCS:%.c=%.d)

ويعرّف المتغير INCLUDES المسارات المراد البحث فيها للعثور على ملفات .h اللازمة لترجمة ملفات .c إلى .o لأي .h. ولا حاجة إلى تحديد المسار إلا إذا لم تكن ملفات .h في دليل العمل الحالي أو في مواقع يفحصها gcc افتراضيًا.

وأثناء مرحلة الربط من الترجمة، يربط المترجم مكتبة الرياضيات -lm ومكتبة محلية -lmylib يعرّفها المتغير LIBS ويجدها وفق تعريفات مسار المكتبات في المتغير LFLAGS التي تحدّد المسارات المراد البحث فيها. ولا حاجة إلى تحديد المسار إلا في الحالات التي لا يكون فيها ملف المكتبة في مواقع يبحث فيها gcc تلقائيًا بشكل افتراضي.

وتشغيل make باستخدام ملف makefile هذا سيولّد أولًا ملفات .d لكل ملف .c لأننا نعرّف CPPFLAGS لتضمّن -MD، أي توليد ملفات تبعية make. وإذا شغّلت ls فسترى ملفات .d هذه:

$ make
$ ls
Makefile ast.c ast.d ast.o codegen.c codegen.d codegen.o ...

وتُضمَّن ملفات التبعية في ملف Makefile في السطر الأخير:

-include $(SRCS:%.c=%d)

ويستخدم هذا قاعدة استبدال لاحقة (SRC:%.c=%d) لتحديد تضمين كل ملف .d، وتكبح - قبل include رسائل الخطأ إذا لم يكن ملف .d موجودًا بالفعل. ولمزيد من التفاصيل، انظر توليد تبعيات makefile تلقائيًا من microHOWTO.

ويستخدم ملف Makefile هذا أيضًا قاعدة make المدمجة لبناء ملفات .o من ملفات .c:

.c.o:
    $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -c $<  -o $@

ولأننا ضبطنا CPPFLAGS لتشمل INCLUDES وكذلك خيارات الأوامر اللازمة لإنشاء ملفات التبعية .d، يمكننا استخدام القاعدة المدمجة.

ولو لم نضف INCLUDES إلى CPPFLAGS، لأمكننا تجاوز القاعدة المدمجة في ملف Makefile بقاعدتنا الخاصة (وأحيانًا يلزم تجاوز أمر مدمج كهذا، لكن هذا ليس مثالًا على ذلك):

...
INCLUDES = -I../includes/ -I.
CPPFLAGS = -MD -MP
...
# override built-in command with our version:
.c.o:
        $(CC) $(CPPFLAGS) $(CFLAGS) $(INCLUDES) -c $<  -o $@

17.5.11. متقدم: مولّدات Makefile

بالنسبة للبرامج الضخمة جدًا، وخصوصًا للبرمجيات التي قد تُثبَّت على أنظمة مختلفة، فإن استخدام برنامج يولّد ملف Makefile لنظام معيّن يجعل إدارة أهداف البناء المختلفة أسهل بكثير. ويُعدّ GNU Automake (جزء من مكتبة GNU Autotools) وCMake مثالين على برامج توليد ملفات makefile. وكلاهما يأخذ مواصفات المستخدم في ملف تهيئة للعثور على المكتبات وإنشاء التبعيات على أنظمة محدّدة، ويولّد ملفات Makefile تلقائيًا لذلك النظام. ولا نناقش أيًّا منهما هنا، لكن قسم المراجع لدينا يحتوي روابط لمزيد من المعلومات عن كليهما.

17.5.12. المراجع

وفيما يلي بعض المراجع لاستخدام make وكتابة ملفات makefile:

من المهام الشائعة البحث عن ملفات محدّدة أو عن محتوى محدّد داخل ملفات. والأمر find مفيد للعثور على ملف أو دليل يحمل اسمه نمطًا معيّنًا، بينما الأمر grep مفيد للبحث عن نمط معيّن داخل ملف أو مجموعة ملفات.

17.6.1. الأمر find

ينفّذ الأمر find بحثًا تعاوديًا في نظام ملفات (من نقطة بداية معيّنة في بنية الأدلة) للعثور على أي ملفات. وعند تشغيله بخيار سطر الأوامر -name ، يبحث find عن جميع الملفات (والأدلة) التي يطابق اسمها نمط البحث المعطى. ونمط البحث تعبير نمطي (regular expression) يشمل محارف حرفية ومحارف خاصة أو أنماطًا فرعية تحدّد أنماطًا عامة. ومن المحارف الخاصة الشائعة في التعبيرات النمطية *. وتعني "طابق مع صفر أو أكثر من أي محرف". لذا في نمط البحث "hello*"، تطابق hello1 وhellothere وhello_123_XYZ النمط جميعًا. وفيما يلي بعض الأمثلة على الأمر find للعثور على الملفات:

$ find ./ -name dog.c    # from current directory (./) find all files named dog.c
$ find ./ -name "*.c"    # from ./ find all files that end with .c
$ find ./ -name "temp*"  # from ./ find all files that that start with temp

وتوجد خيارات سطر أوامر أخرى كثيرة للأمر find تتيح للمستخدمين ضبط سلوك بحثه بطرق مختلفة. انظر صفحة دليله لمزيد من المعلومات.

17.6.2. الأمر grep

يعثر الأمر grep على أنماط داخل ملف أو داخل مجموعة ملفات. ويمكنه ترشيح البيانات من مصدر إلى هدف بناءً على مطابقة الأنماط. ويأخذ الأمر grep وسيطي سطر أوامر، هما تعبير نمطي لنمط البحث، وملف (أو مجموعة ملفات) للبحث فيه عن الأنماط المطابقة. ويُخرج كل سطر في الملف (أو مجموعة الملفات) فيه ظهور مطابق للنمط. ومثل find، يملك grep أيضًا خيارات سطر أوامر كثيرة لتخصيص بحثه. وفيما يلي بعض الأمثلة على الأمر grep للعثور على أنماط في ملف (أو ملفات). ومن الملفات المستخدمة في هذه الأمثلة ملف temp الذي محتواه التالي:

hello
Hello
hiHelo
hello there
hello There
Meow
Mellow

للبحث عن جميع ظهورات "hello" في ملف temp:

$ grep hello temp
hello
hello there
hello There

للبحث عن ظهورات "hello there" في ملف temp. في هذا المثال، بما أن نمط البحث يحتوي محارف مسافة، يجب أن يكون النمط داخل علامتي اقتباس مزدوجتين.

$ grep "hello there" temp
hello there

لتضمين أرقام أسطر الملف مع المطابقات (خيار سطر الأوامر -n) وتجاهل حالة الأحرف في النمط (خيار سطر الأوامر -i):

$ grep -n -i "hello there" temp
4:hello there
5:hello There

للبحث عن جميع الأنماط التي فيها أي محرف أبجدي كبير ([A-Z]) متبوعًا بـel:

$ grep "[A-Z]el" temp
Hello
hiHelo
Mellow

للبحث عن جميع الكلمات التي تبدأ بـ(\b يطابق حدّ كلمة) أي محرف أبجدي كبير ([A-Z]) متبوعًا بـel:

$ grep "\b[A-Z]el" temp
Hello
Mellow

للبحث عن جميع الأنماط التي فيها أي محرف أبجدي كبير ([A-Z]) متبوعًا بصفر أو أكثر (*) من محارف e:

$  grep "[A-Z]e*" temp
Hello
hiHelo
hello There
Meow
Mellow

لاحظ أن hello There مطابقة لأنها تحتوي محرفًا كبيرًا هو T متبوعًا بصفر من محارف e.

للبحث عن جميع الأنماط التي فيها أي محرف أبجدي كبير ([A-Z]) متبوعًا بـel متبوعًا بصفر أو أكثر (*) من محارف l الإضافية متبوعًا بـo:

$  grep "[A-Z]ell*o" temp
Hello
hiHelo
Mellow

للبحث عن main في جميع الملفات المنتهية بـ.c:

$ grep main *.c
function.c:int main(void) {
hello.c:int main(void) {
string.c:int main(void) {
structfunc.c:int main(void) {
types_scanf.c:int main(void) {

للبحث عن main في جميع الملفات المنتهية بـ.c، مع سرد اسم الملف (-H) ورقم السطر (-n) لجميع المطابقات:

$ grep -H -n main *.c
function.c:21:int main(void) {
hello.c:13:int main(void) {
string.c:11:int main(void) {
structfunc.c:17:int main(void) {
types_scanf.c:13:int main(void) {

للبحث تعاوديًا (-r) عن hello في جميع الملفات بدءًا من الدليل الفرعي CS31:

$ grep -r hello  ~/cs31

وتوجد خيارات سطر أوامر كثيرة لـgrep لضبط بحثه بطرق مختلفة.

17.6.3. المراجع

لمزيد من المعلومات انظر:

يخضع كل ملف (ودليل) في Unix لمجموعة من الصلاحيات تحدّد حقوق الوصول إليه. والصلاحيات المرتبطة بملف هي:

  • r: صلاحية القراءة من الملف أو الدليل
  • w: صلاحية الكتابة في الملف أو الدليل
  • x: صلاحية التنفيذ للملف أو الدليل (وبالنسبة للدليل، يعني ذلك أنه يمكنك الانتقال إليه بـcd، وبالنسبة للملف يعني أنه يمكنك تنفيذه على سطر الأوامر (مثل ./filename). ويضبط المترجم تلقائيًا الملف التنفيذي الهدف بصلاحية x للمالك.

كما تحدّد صلاحيات كل ملف ثلاث مجموعات من المستخدمين: مستخدم/مالك الملف (u)؛ والمستخدمون في المجموعة نفسها التي ينتمي إليها الملف (g)؛ والآخرون (o) أو كل من عداهم. وبتغيير صلاحيات الملف، يمكنك حماية الملفات أو مشاركتها بطرق مختلفة مع هذه المجموعات المختلفة من المستخدمين. فمثلًا، يمكن ضبط صلاحيات الملف لتمنح المالك صلاحيتي القراءة والكتابة، والمستخدمين في مجموعة الملف صلاحية القراءة، وكل من عداهم لا صلاحية قراءة ولا كتابة.

ولسرد صلاحيات الملفات مع الملفات والأدلة، استخدم خيار سطر الأوامر -l مع ls:

$ls -l
drwx------ 3 sam users 4096 Mar 26 12:49 classes/
drwx------ 2 sam users 4096 Mar 26 12:54 letters/
-rw-r----- 1 sam pals    78 Mar 26 12:51 notes
drwx------ 2 sam users 4096 Mar 26 12:48 projects/

وتكون مخرجات ls -l بالصيغة التالية (مع عرض سرد notes كمثال):

type   permissions  links  owner  group  size  modification date   name
       u   g   o
 -    rw- r-- ---    1     sam    pals   78    Mar 26 12:51       notes
 d    rwx --- ---    3     sam    users 4096   Mar 26 12:49       classes/

يكون type مساويًا d للأدلة و- للملفات العادية. وتأتي قوائم صلاحيات الملف في ثلاث مجموعات من rwx تحدّد صلاحيات مستخدم/مالك الملف (u) والمجموعة (g) والآخرين (o). فمثلًا، تعني -rw-r----- المرتبطة بملف notes أن مالك الملف sam يملك صلاحيتي القراءة والكتابة على الملف (rw-)، وأن مجموعة الملف pals تملك صلاحية القراءة فقط (r--)، وأن الآخرين لا يملكون أي صلاحيات للوصول إلى الملف (---). وتُظهر الحقول الأخرى في السرد اسم مستخدم مالك الملف (sam)، واسم مجموعة الملف (pals)، وحجم الملف بالبايتات (78)، وآخر وقت عُدّل فيه الملف (Mar 26 12:51)، واسم الملف (notes). كما يُسرد عدد الروابط الصلبة إلى الملف (1)، وهو عدد الأسماء في بنية الأدلة التي تشير إلى هذا الملف نفسه.

وسرد classes هو drwx------، ما يعني أن classes دليل، وأن مالك الملف sam يملك صلاحيات القراءة والكتابة والتنفيذ (rwx)، وأن مجموعة الدليل (users) والآخرين لا يملكون أي صلاحيات وصول (---). وبعبارة أخرى، هذا دليل خاص بالمستخدم sam؛ فلا يمكن لأي أحد آخر الوصول إليه. ونلاحظ أن الدليل يجب أن تكون صلاحية التنفيذ (x) مضبوطة فيه للانتقال إليه بـcd. وللأدلة أحجام روابط أكبر من 1 لأن اسم الدليل (مثل classes) والاسم ./ داخل الدليل (والاسم ../ في أي من أدلته الفرعية) يشيران إلى الدليل نفسه. ولأن classes فيه دليل فرعي واحد هو CS31، فإن قيمة رابطه 3.

17.7.1. chmod

يغيّر الأمر chmod صلاحيات ملف أو دليل. ويأخذ وسائط سطر أوامر تحدّد كيفية تغيير الصلاحيات لمجموعات المستخدمين الثلاث المرتبطة بكل ملف: المستخدم/المالك، والمجموعة، والآخرون.

وإحدى طرق تشغيل chmod إعطاء رقم من ثلاثة أرقام كوسيط سطر أوامر يرمّز كيفية ضبط الصلاحيات. ويحدّد الرقم الأول صلاحيات مستخدم/مالك الملف، ويحدّد الرقم الثاني صلاحيات مجموعة الملف، والثالث جميع المستخدمين الآخرين. فمثلًا، يجعل chmod 700 letters الدليل letters خاصًا؛ فـ7 تضبط صلاحيات المستخدم على rwx، و0 تضبط الصلاحيات على --- للمجموعة والآخرين.

ولفهم معنى الأرقام، تأمّل كل رقم ممثّلًا كعدد ثنائي من ثلاث بتات، حيث تعني 1 منح الصلاحية وتعني 0 عدم منحها للمجموعة rwx. ويعرض الجدول 1 بعض الأمثلة.

الرقم الثنائي الصلاحية
7 111 ضبط صلاحيات rwx
5 101 ضبط صلاحيات r وx فقط
4 100 ضبط صلاحية r فقط
6 110 ضبط صلاحيات rw فقط
0 000 إلغاء جميع الصلاحيات…لا وصول

فمثلًا، إذا أراد sam منح صلاحية القراءة لملف notes لجميع المستخدمين الآخرين ومنح نفسه صلاحيتي القراءة والكتابة، فسيشغّل الأمر التالي:

$ chmod 644 notes
$ ls -l
drwx------ 3 sam users  4096 Mar 26 12:49 classes/
drwx------ 2 sam users  4096 Mar 26 12:54 letters/
-rw-r--r-- 1 sam pals     78 Mar 26 12:51 notes
drwx------ 2 sam users  4096 Mar 26 12:48 projects/

تضبط 644 صلاحيات notes على -rw-r—​r--: فـ6 تمنح مالك الملف وصول rw-؛ و4 تمنح مجموعة الملف وصول r--؛ و4 تمنح الآخرين وصول r--.

ويمكن للأمر chmod أيضًا استخدام صيغة الضبط [u,g,o][+,-][permissions]. فمثلًا، إذا أراد sam منح مجموعة users صلاحية الكتابة على دليل projects، يمكنه تحديد هذه الصلاحية بـg+w (إضافة صلاحية الكتابة للمجموعة)، وإذا أراد إزالة صلاحية القراءة من notes عن الآخرين، يمكنه تحديد o-r (إزالة صلاحية القراءة من الجميع):

$ chmod g+w projects
$ chmod o-r notes
$ ls -l
drwx------ 3 sam users 4096 Mar 26 12:49 classes/
drwx------ 2 sam users 4096 Mar 26 12:54 letters/
-rw-r----- 1 sam pals    78 Mar 26 12:51 notes
drwx-w---- 2 sam users  4096 Mar 26 12:48 projects/

وعندما يدرك أن منح مجموعة users صلاحية الكتابة على دليل projects كان فكرة سيئة، يمكنه إزالتها باستخدام g-w:

$ chmod g-w projects
$ ls -l projects
drwx------ 2 sam users  4096 Mar 26 12:48 projects/

وللأمر chmod خيارات سطر أوامر أخرى، منها راية تعاودية لتغيير الصلاحيات تعاوديًا على محتويات دليل فرعي كامل. انظر صفحة الدليل لمزيد من التفاصيل.

كما يدعم Linux قوائم التحكم في الوصول (access control lists, ACLs)، التي تتيح ربط مجموعة أغنى بكثير من الصلاحيات بالملفات والأدلة. وتحتوي صفحة دليل getfacl وsetfacl على معلومات عن قوائم ACL، لكن تفاصيلها تتجاوز نطاق تغطيتنا.

17.7.2. chgrp

يمكن استخدام الأمر chgrp لتغيير المجموعة المرتبطة بملف أو دليل. وغالبًا ما تحتاج مجموعات Unix إلى إنشاء من مدير نظام يملك صلاحية الجذر على النظام (فمستخدم الجذر هو حساب المستخدم الفائق في Unix الذي يملك حقوق وصول إلى نظام الملفات لا يملكها المستخدم العادي).

وإذا كان المستخدم sam جزءًا من مجموعة اسمها proj3 مع مستخدمين آخرين، فيمكن لـsam تغيير المجموعة وضبط الصلاحيات على دليلهم projects لمنح هذه المجموعة فقط حقوقًا مرتفعة على الدليل. فمثلًا:

$ chgrp proj3 projects  # change the projects directory's group to proj3
$ ls -l
drwx------ 3 sam users  4096 Mar 26 12:49 classes/
drwx------ 2 sam users  4096 Mar 26 12:54 letters/
-rw-r----- 1 sam pals     78 Mar 26 12:51 notes
drwx------ 2 sam proj3 4096 Mar 26 12:48 projects/

$ chmod 750 projects    # give proj3 group members r and x access to directory
$ ls -l
drwx------ 3 sam users  4096 Mar 26 12:49 classes/
drwx------ 2 sam users  4096 Mar 26 12:54 letters/
-rw-r----- 1 sam pals     78 Mar 26 12:51 notes
drwxr-x--- 2 sam proj3 4096 Mar 26 12:48 projects/

وبالمثل، يغيّر الأمر chown مالك ملف، لكنك تحتاج إلى صلاحيات الجذر لتشغيله.

ملاحظة — ضبط مجموعة الملف والصلاحيات الافتراضية

لكل مستخدم صلاحيات افتراضية ومجموعة افتراضية ستُرتبط تلقائيًا بجميع الملفات والأدلة الجديدة التي ينشئها المستخدم. ويمكن تعديل كلٍّ منهما في ملف تهيئة الصدفة لديك (مثل .bashrc). انظر القسم 17.14 لمزيد من المعلومات عن ملفات النقطة.

مجموعتك الافتراضية محدّدة في متغير بيئة الصدفة المسمّى GROUP. ويمكنك سرد قيمته باستخدام echo $GROUP:

$ echo $GROUP
GROUP=users

وصلاحيات ملفك الافتراضية تُهيَّأ من إعداد umask في ملف .bashrc لديك. ويمكنك سرد قيمته باستخدام umask:

$ umask
77

وumask بقيمة 77 (أو 077) تجعل جميع ملفاتي وأدلتي خاصة (فهي تضبط صلاحيات المجموعة والآخرين على 00 أو ------). ويمكنك اعتبار ترميز umask شبيهًا نوعًا ما بعكس ترميز chmod (فمثلًا 7 تزيل جميع الصلاحيات (rwx) في umask لكنها تمنحها كلها في chmod).

17.7.3. المراجع

لمزيد من المعلومات انظر:

تُستخدم أداة tar (أرشيف الشريط) لحزم مجموعة ملفات في ملف واحد (ملف أرشيف أو ملف tar). ويُستخدم الأمر tar أيضًا لاستخراج الملفات من ملف tar. ويحتوي ملف الأرشيف (أو ملف tar) على جميع بيانات الملفات التي يتكوّن منها ملف tar بالإضافة إلى بيانات وصفية إضافية تصف محتوياته. والبيانات الوصفية ضرورية لـtar لاستخراج الملف أو الملفات الفردية من ملف tar.

وغالبًا ما تُستخدم ملفات tar لتوزيع مجموعة ملفات أو نقل مجموعة ملفات من جهاز إلى آخر. وقد تُستخدم أيضًا لتخزين نسخ احتياطية من مجموعات ملفات (أرشيفات الملفات). فمثلًا، إذا أراد مستخدم نقل مجموعة ملفات من نظام إلى آخر، فسيشغّل أولًا tar بخيار سطر الأوامر -c لإنشاء ملف tar واحد يحتوي جميع الملفات، ثم ينسخ ملف tar الناتج بـscp إلى الجهاز البعيد. وعلى الجهاز البعيد سيشغّل بعد ذلك tar بالخيار -x لاستخراج الملفات من ملف tar.

وللأمر tar خيارات سطر أوامر كثيرة لضبط سلوكه بطرق مختلفة. ويسرد الجدول 1 بعض الأمثلة.

الخيار الوصف
-c إنشاء ملف tar من مجموعة ملفات
-x استخراج ملف tar (فكّ الأرشيف)
-t سرد محتويات ملف tar
-v النمط المطوّل، يطبع معلومات أثناء تشغيل الأمر
-f يحدّد اسم ملف tar

ولإنشاء ملف tar تحتاج إلى استخدام -c، وتحديد اسم ملف خرج tar بالخيار -f، ثم إعطاء قائمة الملفات (والأدلة) المراد تضمينها في ملف tar (وستُضمَّن محتويات الدليل كاملة).

وهذا مثال على كيفية إنشاء ملف tar من ثلاثة ملفات عادية (main.c وprocess.c وMakefile) ومحتويات دليل (inputfiles/):

$ tar -c -v -f mytarfile.tar  main.c process.c Makefile intputfiles/

و-v هو خيار سطر الأوامر المطوّل. وهو يسرد بعض المعلومات عن الملفات التي يجري أرشفتها أثناء تنفيذ tar.

ويمكن أيضًا تحديد خيارات سطر أوامر tar بصياغة بديلة أكثر إحكامًا (وهي تُستخدم غالبًا في أمثلة tar التي تراها):

$ tar -cvf mytarfile.tar  main.c process.c Makefile intputfiles/

$ tar cvf mytarfile.tar  main.c process.c Makefile intputfiles/

وبدلًا من ذلك، يمكنك إنشاء دليل ونسخ الملفات التي تريد أرشفتها إليه، وتوليد ملف tar من ذلك الدليل. فمثلًا:

$ mkdir mytardir
$ cp *.c mytardir/.
$ cp -r inputfiles mytardir/.
$ tar cvf mytarfile.tar mytardir

ولسرد الملفات في ملف tar، استخدم خيار سطر الأوامر -t:

$ tar -tvf mytarfile.tar

ولاستخراج الملفات من ملف tar، استخدم خيار سطر الأوامر -x:

$ tar xvf mytarfile.tar

17.8.1. ضغط الملفات

كثيرًا ما يُستخدم ضغط الملفات مع tar لتقليل حجم ملف tar. وهذا مفيد لتقليل مساحة التخزين المطلوبة لملف tar، وكذلك لتقليل كمية البيانات المرسلة عبر الشبكة عند نسخ ملف tar إلى جهاز بعيد باستخدام scp. ويُستخدم gzip وgunzip لضغط ملف وفكّ ضغطه بضغط gzip، ويُستخدم bzip2 وbunzip2 لضغط ملف وفكّ ضغطه بضغط bzip. وفيما يلي بعض الاستدعاءات النموذجية:

$ ls -l
-rw-r--r-- 1 sarita users 40840 Apr 12 16:55 file

$ gzip file        # compress using gzip
$ ls -l
-rw-r--r-- 1 sarita users 13258 Apr 12 16:55 file.gz

$ gunzip file.gz   # uncompress a gziped file
$ ls -l
-rw-r--r-- 1 sarita users 40840 Apr 12 16:55 file

$ bzip2 file        # compress using bzip2
$ ls -l
-rw-r--r-- 1 sarita users 12568 Apr 12 16:55 file.bz2

$ bunzip2 file.bz2   # uncompress a bzip2ed file
$ ls -l
-rw-r--r-- 1 sarita users 40840 Apr 12 16:55 file

tar وضغط الملفات

غالبًا ما يُستخدم ضغط الملفات على ملفات tar. ويمكن للمستخدم تشغيل أدوات ضغط الملفات على ملف tar بعد أن ينشئه tar، وتشغيل أدوات فكّ الضغط على ملف tar قبل تشغيل tar لاستخراج الملفات منه. غير أنه بما أن ضغط الملفات يُستخدم كثيرًا مع ملفات tar، فإن لـtar خيارات سطر أوامر لإنشاء ملفات tar مضغوطة (واستخراج الملفات من ملفات tar مضغوطة) باستخدام خوارزميات ضغط مختلفة (فمثلًا -j يستخدم bzip2، و-z يستخدم gzip).

وفيما يلي بعض الأمثلة على ضغط ملف tar في خطوة واحدة باستخدام الخيار -z لـgzip أو الخيار -j لـbzip مع tar:

# use -z option to compress tar file using gzip:
$ tar -czvf myproj.tar.gz myproj/
$ ls
  myproj/ myproj.tar.gz

# or use -j option to compress tar file using bzip2:
$ tar -cjvf myproj.tar.bz2 myproj/
$ ls
  myproj/ myproj.tar.bz2

وبدلًا من ذلك، إليك مثال على فصل إنشاء ملف tar وضغطه إلى خطوتين (إنشاء ملف tar، ثم الضغط باستخدام gzip (أو bzip2):

$ tar cvf myproj.tar myproj/   # create a tar file from myproj
$ ls
  myproj/ myproj.tar

$ gzip myproj.tar     # gzip the tar file
$ ls
  myproj/ myproj.tar.gz

# OR could use bzip
$ bzip2 myproj.tar     # bzip the tar file
$ ls
  myproj/ myproj.tar.bz2

ويمكنك أيضًا استخدام الطريقة أحادية الخطوة أو ثنائية الخطوة لاستخراج الملفات من ملف tar مضغوط. فمثلًا، هذه هي طريقة الخطوة الواحدة (-z تحدّد فكّ ضغط الملف أولًا باستخدام gzip):

$ tar -xzvf myproj.tar.gz
$ ls
  myproj/ myproj.tar.gz

وبدلًا من ذلك، هذه هي الطريقة ثنائية الخطوة لاستخراج الملفات من ملف tar مضغوط (فكّ الضغط أولًا بـgunzip أو bunzip2، ثم فكّ الأرشيف):

$ ls
  myproj.tar.gz
$ gunzip myproj.tar.gz
$ ls
  myproj.tar
$ tar -xvf myproj.tar
$ ls
  myproj/ myproj.tar

# or using bunzip2 for bzip'ed file
$ ls
  myproj.tar.bz2
$ bunzip myproj.tar.bz2
$ ls
  myproj.tar
$ tar -xvf myproj.tar
$ ls
  myproj/ myproj.tar

17.8.2. جمع كل ذلك معًا

وفيما يلي مثال على استخدام tar لنقل محتويات دليل من جهاز (laptop) إلى جهاز بعيد (ada.cs.college.edu). ويستخدم الأمر scp لنسخ الملف عن بُعد بين الجهازين (انظر القسم 17.3 لمزيد من المعلومات عن scp).

أولًا، ينشئ المستخدم ملف tar المضغوط على laptop (ملاحظة: نعرض الطريقة ثنائية الخطوة لإنشاء ملف tar وضغطه لنُظهر سرد الملفات في ملف tar، لكن يمكن أيضًا إنشاء ملف tar المضغوط في خطوة واحدة باستخدام خيارات سطر الأوامر -cjvf):

laptop$  ls myproj/
  Makefile input1.txt input2.txt main.c mux.c mux.h

laptop$  tar -cvf myproj.tar myproj/   # create a tar file from myproj
laptop$  ls
  myproj/ myproj.tar

laptop$  tar -tf myproj.tar            # list tar file contents
   myproj/
   myproj/Makefile
   myproj/input1.txt
   myproj/input2.txt
   myproj/main.c
   myproj/mux.c
   myproj/mux.h

laptop$ gzip myproj.tar              # compress the file

ثم ينسخون ملف tar الواحد إلى الجهاز البعيد بـscp:

laptop$ scp myproj.tar sam@ada.cs.college.edu:.   # scp single tar file
  sam@ada.cs.college.edu's password:

ثم يمكن للمستخدم الدخول إلى الجهاز البعيد بـssh، وفكّ أرشيف ملف myproj.tar لاستخراج الملفات:

laptop$ ssh sam@ada.cs.college.edu
  sam@ada.cs.college.edu's password:

ada$ ls
 classes/ letters/  myproj.tar projects/

ada$ tar -xjvf myproj.tar          # extract contents of tar file

ada$ ls
 classes/ letters/  myproj/  myproj.tar projects/

وإذا أراد المستخدم فكّ أرشيف الدليل في مكان غير دليله الرئيسي، فيمكنه استخدام mv لنقل الدليل إلى حيث يريد (أو كان يمكنه أيضًا نقل ملف tar إلى حيث يريده قبل فكّ أرشيفه). فمثلًا، لنقله كدليل فرعي تحت دليل project:

ada$ ls
   classes/ letters/  myproj/  myproj.tar projects/
ada$ mv myproj project/.
ada$ ls
   classes/ letters/  myproj.tar projects/
ada$ cd project
ada$ ls
   myproj/

وغالبًا ما يزيل المستخدم ملف .tar بعد فكّ أرشيف ملف tar:

rm myproj.tar
ls

17.8.3. المراجع

لمزيد من المعلومات انظر:

عندما تشغّل برنامجًا أو أمرًا عند مطالبة الصدفة، تنشئ الصدفة عادةً عملية جديدة ستشغّل البرنامج أو الأمر (والعملية هي تجريد نظام التشغيل لبرنامج قيد التنفيذ). وهناك بعض أوامر الصدفة المدمجة التي تنفّذها عملية الصدفة نفسها (مثل cd)، لكن معظمها يعمل كعمليات منفصلة. انظر الفصل 13: نظام التشغيل لمزيد من المعلومات عن العمليات.

وتدعم صدفة Unix ميزات التحكم في العمليات، وهي طرق لتحديد كيفية تشغيل الصدفة لبرنامج، وطرق للتفاعل مع العملية أثناء تشغيلها.

17.9.1. التشغيل في المقدمة والخلفية

عند تشغيل برنامج عند مطالبة الصدفة، فإنه يعمل في المقدمة افتراضيًا، أي أن البرنامج يعمل حتى الاكتمال قبل أن يعمل برنامج الصدفة مرة أخرى ويطبع المطالبة التالية. غير أنه يمكن أيضًا تشغيل البرامج في الخلفية، أي أن الصدفة تبدأ تشغيل البرنامج لكنها لا تنتظر اكتماله قبل طباعة المطالبة التالية؛ وهذا يتيح للمستخدم إدخال أمر آخر بينما لا يزال البرنامج قيد التشغيل. ولإخبار الصدفة بتشغيل برنامج في الخلفية، يضيف المستخدم & في نهاية سطر الأمر. فمثلًا، هكذا تخبر الصدفة بتشغيل برنامج a.out في الخلفية:

$ ./a.out &
$

ويمكن استخدام الأمر ps لسرد جميع البرامج العاملة في الصدفة. فمثلًا، هنا نبدأ تشغيل برنامجي a.out في الخلفية، ثم نكتب ps:

$ ./a.out &
$ ./a.out &
$ ps
    PID TTY          TIME CMD
 2729   pts/72    0:03.91 bash
 3582   pts/72    0:00.00 ./a.out
 3583   pts/72    0:00.00 ./a.out
 3584   pts/72    0:00.00 ps

في مخرجات المثال، يمكنك رؤية عمليتي a.out تعملان بالإضافة إلى عملية صدفة Bash وعملية ps. ويُسرد مع كل عملية معرّف عمليتها (process id)، وهو معرّف فريد يحتفظ به نظام التشغيل لكل عملية (أي أن معرّف عملية bash هو 2729، ومعرّفا العمليتين المشغّلتين لبرنامج a.out هما 3582 و3583).

ويمكن نقل عملية تعمل في الخلفية إلى المقدمة باستخدام الأمر fg:

$ ./a.out &     # run a.out in the background
$ ps
    PID TTY          TIME CMD
 2729   pts/72    0:03.91 bash
 3584   pts/72    0:00.00 ./a.out

$ fg           # move a.out to the foreground (the shell waits for it to exit)

$ ps           # run ps at the shell prompt (a.out has exited)
    PID TTY          TIME CMD
 2729   pts/72    0:03.91 bash

وبكتابة CTRL-Z يمكن مقاطعة عملية تعمل في المقدمة وتعليقها، أي إيقاف تنفيذها مؤقتًا (فهي لا تعمل). ويؤدي ذلك إلى أن تعمل الصدفة مرة أخرى وتطبع مطالبة الصدفة. وإذا كتب المستخدم bg، تواصل العملية المعلّقة العمل، لكنها تعمل الآن في الخلفية. وإذا كتب المستخدم fg، تواصل العملية المعلّقة العمل في المقدمة مرة أخرى. وفيما يلي بعض الأمثلة:

$ ./a.out     # run a.out in the foreground
              # type CTRL-Z to suspend a.out
[1]  Stopped                 ./a.out

$ ps
    PID TTY          TIME CMD
 2729   pts/72    0:03.91 bash
 3588   pts/72    0:00.00 ./a.out

$ bg         # unsuspend a.out and run in background

$ ps
    PID TTY          TIME CMD
 2729   pts/72    0:03.91 bash
 3588   pts/72    0:00.00 ./a.out

$ fg        # run backgrounded process in the foreground

17.9.2. إنهاء العمليات قيد التشغيل

يُستخدم الأمران kill وpkill لإرسال إشارة من عملية الصدفة إلى عمليات أخرى. والإشارات طريقة يمكن لعملية أن تُخطر بها عملية أخرى بفعل شيء ما. وتُفعِّل كل إشارة سلوكًا افتراضيًا، والخروج أو التجاهل خياران افتراضيان شائعان (انظر الفصل 13: نظام التشغيل لمزيد من المعلومات عن الإشارات).

ويأخذ الأمر kill معرّف العملية (PID) للعملية المراد إرسال الإشارة إليها، ويأخذ الأمر pkill اسم الأمر أو الملف التنفيذي المراد إرسال الإشارة إليه (pkill يرسل الإشارة إلى جميع العمليات العاملة بالاسم المحدّد التي يملكها المستخدم الذي يشغّل pkill). ويأخذ كلٌّ من kill وpkill وسيط سطر أوامر اختياريًا يحدّد الإشارة المعيّنة المراد إرسالها إلى العملية. وبدون تحديد الإشارة المعيّنة، يُرسل كلاهما افتراضيًا إشارة إنهاء (SIGTERM) إلى العملية أو العمليات المحدّدة.

ومن الاستخدامات الشائعة لهذين الأمرين إرسال إشارة إلى عملية تخبرها بالخروج (ومن هنا جاء اسم kill غير المحظوظ). لاحظ أن عملية تعمل في المقدمة يمكن إنهاؤها أيضًا بكتابة CTRL-C، ما يرسل إلى العملية العاملة في المقدمة إشارة SIGINT التي تخبر العملية افتراضيًا بالخروج.

وهذا مثال على استدعاء pkill لإنهاء جميع عمليات a.out:

$ ps
    PID TTY          TIME CMD
3032597 pts/72   00:00:00 bash
3033074 pts/72   00:00:04 a.out
3033076 pts/72   00:00:03 a.out
3033089 pts/72   00:00:02 a.out
3033105 pts/72   00:00:00 ps

$ pkill a.out
[1]   Terminated              ./a.out
[2]-  Terminated              ./a.out
[3]+  Terminated              ./a.out

$ ps
    PID TTY          TIME CMD
3032597 pts/72   00:00:00 bash
3033128 pts/72   00:00:00 ps

وهذا مثال على استدعاء kill لإنهاء عملية a.out معيّنة بمعرّفها (العملية الثانية في سرد ps الأول):

$ ps
    PID TTY          TIME CMD
3032597 pts/72   00:00:00 bash
3033870 pts/72   00:00:03 a.out
3033883 pts/72   00:00:02 a.out
3033886 pts/72   00:00:01 a.out
3033897 pts/72   00:00:00 ps

$ kill 3033883
[2]-  Terminated              ./a.out

$ ps
    PID TTY          TIME CMD
3032597 pts/72   00:00:00 bash
3033870 pts/72   00:00:18 a.out
3033886 pts/72   00:00:16 a.out
3033974 pts/72   00:00:00 ps

لاحظ أن kill أنهى فقط العملية المعيّنة ذات قيمة المعرّف المطابقة، لا كل عملية تشغّل البرنامج نفسه (a.out).

وفي هذين المثالين، لا نحدّد الإشارة المراد إرسالها. وبدون أي خيارات سطر أوامر اختيارية، يرسل kill وpkill إلى العملية إشارة الإنهاء (SIGTERM أو رقم الإشارة 15). ويخبر SIGTERM افتراضيًا العملية المُشار إليها بالخروج. وأحيانًا سترى استدعاءات لـkill وpkill تتضمّن رقم إشارة مختلفًا. وفي هذه الحالة يُحدَّد رقم الإشارة كوسيط سطر أوامر (-'). فمثلًا، كان يمكننا تضمين رقم الإشارة -9لإرسال إشارةSIGKILL` إليهما (وهي طريقة أخرى لإخبار عملية بالخروج):

$ pkill -9 a.out
$ kill -9 3033883

وبشكل عام، يمكن استخدام هذين الأمرين لإرسال أي إشارة إلى عملية. فمثلًا، لإرسال إشارة SIGALRM إلى عملية (وهي رقم الإشارة 14)، يمكنك فعل هذا:

$ kill -14 3033883

وإذا لم يكن للعملية معالج إشارة لإشارة SIGALRM، فالسلوك الافتراضي هو تجاهل إشارة SIGALRM فحسب.

وتسرد صفحة دليل signal الحد الأدنى من الإشارات (وأرقامها) المعرّفة في النظام: man 7 signal.

17.9.3. المراجع

لمزيد من المعلومات انظر:

غالبًا ما يريد المستخدمون جمع معلومات توقيت عن برامجهم. فمثلًا، قد يريد مستخدم قياس ما إذا كان تغيير في برنامج يحسّن أداءه. والأمر time إحدى طرق جمع معلومات التوقيت عن عملية. ويأخذ وسيط سطر أوامر هو سطر الأمر التنفيذي للبرنامج أو الأمر المراد توقيته، ويُخرج معلومات توقيت تنفيذ البرنامج أو الأمر. وهذا مثال على توقيت تشغيل الملف التنفيذي للبرنامج gol الذي يأخذ وسيطي سطر أوامر (oscillator_500_500.txt و0):

$ time ./gol oscillator_500_500.txt 0
real	0m3.814s
user	0m3.725s
sys	0m0.016s

وتُعطى مخرجات التوقيت كثلاث قيم زمنية مختلفة: real هو زمن التنفيذ الإجمالي، ويُشار إليه أحيانًا بالزمن الجداري لأنه يقابل الزمن من البداية إلى الاكتمال كما لو كان المرء يقيس زمن التشغيل بساعة؛ وuser هو الجزء من الزمن الإجمالي الذي قضته العملية في التنفيذ في نمط المستخدم؛ وsys هو الجزء من الزمن الذي قضته العملية في التنفيذ في نمط النظام (انظر الفصل 13: نظام التشغيل لمزيد من المعلومات عن نمطي المستخدم والنظام). لاحظ أن زمن user زائد زمن sys لا يساوي زمن real. والسبب أن العملية تكون أثناء جزء من زمن تنفيذها الإجمالي محجوبة في انتظار أحداث معيّنة مثل الإدخال/الإخراج أو انتظار دورها لتُجدولها نظام التشغيل لتسير على نواة CPU. وعندما تكون العملية محجوبة، فإنها لا تجمع زمن user ولا sys، لكنها تجمع زمن real فعلًا.

وفي هذا المثال، تقضي عملية gol الموقوتة معظم وقتها في العمل على نواة CPU (فزمن user + sys لديها يكاد يكون كل زمن real). وبالنسبة للتطبيقات التي تنفّذ إدخال/إخراج كثيرًا، قد يكون زمن user + sys أقل بكثير من زمن real.

وغالبًا ما يُستخدم الخيار -p مع الأمر time. فهو يعرض مخرجات التوقيت بصيغة متوافقة مع معيار POSIX، بحيث يعرض كل نظام نتائج التوقيت بالصيغة نفسها عند تشغيل time بالخيار -p. وهذا مفيد بشكل خاص لكتابة برامج تنفّذ معالجة لاحقة لمخرجات time (مثل حساب المتوسط على مجموعة تشغيلات موقوتة للبرنامج) وتعمل بشكل مطابق عبر الأنظمة.

17.10.1. المراجع

لمزيد من المعلومات انظر:

يسرد الأمر history السجل الحديث للأوامر التي شغّلتها الصدفة. وقد يكون من المفيد أحيانًا للمستخدم رؤية الأوامر التي شُغّلت في الماضي. فمثلًا، لتذكّر وسائط سطر الأوامر المعيّنة التي استخدموها مع برنامج. كما يتيح للمستخدم إعادة تنفيذ الأوامر بترميز اختصاري خاص لإعادة تشغيل أمر معيّن من سجل الصدفة.

وهذا مثال على تشغيل الأمر history:

$ history
    35	8:17	ls
    36	8:17	cd classes/CS31
    37  8:17    echo "hi Sam"
    38	8:17	./a.out infile.txt outfile.txt &
    39	8:18	./a.out &
    40	8:18	ps
    41	8:18	pkill a.out
    42	8:18	ps
    43	8:18	history

يعرض هذا المثال سلسلة من بضعة أوامر شُغّلت في سجل الصدفة الحديث. ومع كل أمر في السجل رقم يدل على موضعه في السجل والوقت الذي بدأ فيه الأمر. وكان آخر أمر شُغّل هو الأمر رقم 43، history. ولإعادة تشغيل أمر من السجل، يمكنك استخدام ! متبوعة برقم الأمر، وتعيد !! تشغيل الأمر السابق الأحدث مرة أخرى. وهذا مفيد بشكل خاص لإعادة تشغيل الأوامر التي لها قائمة طويلة من وسائط سطر الأوامر بسهولة. فمثلًا، لإعادة تشغيل الأمر 37 من السجل (echo "hi Sam")، يحتاج المستخدم فقط إلى كتابة !37 عند مطالبة الصدفة:

$ !37
hi Sam
$ history
    35	8:17	ls
    36	8:17	cd classes/CS31
    37  8:17    echo "hi Sam"
    38	8:17	./a.out infile.txt outfile.txt &
    39	8:18	./a.out &
    40	8:18	ps
    41	8:18	pkill a.out
    42	8:18	ps
    43	8:18	history
    44  8:20    echo "hi Sam"
    45	8:20	history

لاحظ أن !37 لا يظهر في السجل، لكن رقم الأمر 44 من السجل يسرد سطر الأمر نفسه الذي يسرده سطر الأمر 37، وهو الذي أُعيد تشغيله بإدخال 37!.

17.11.1. المراجع

لمزيد من المعلومات انظر:

تبدأ كل عملية بثلاثة تدفقات ملفات مفتوحة: الإدخال القياسي (stdin)، والإخراج القياسي (stdout)، والخطأ القياسي (stderr). وإلى جانب توفير واجهة ملفات للوصول إلى الملفات المخزّنة، يوفّر Unix واجهة ملفات لجميع التجريدات التي ينفّذها، بما فيها الأجهزة مثل الطرفية أو لوحة المفاتيح. وتدفق الملف stdin هو المكان الذي يُقرأ منه إدخال العملية، وstdout وstderr هما المكان الذي يُكتب فيه خرج العملية وخرج أخطائها. ويقرأ stdin افتراضيًا من لوحة المفاتيح، ويكتب stdout وstderr إلى الطرفية. غير أنه يمكنك تغيير مصدر stdin وstdout وstderr لبرنامج أو جهة إعادة توجيهها باستخدام إعادة توجيه الإدخال/الإخراج.

ولكل من stdin وstdout وstderr واصف ملف (file descriptor) مرتبط به. وواصف الملف قيمة صحيحة موجبة تشير إلى ملف مفتوح. وينفّذ Unix واجهة ملفات لأشياء كثيرة ليست ملفات وأدلة عادية. ومن الأمثلة على ذلك واجهة ملفات للأجهزة مثل لوحة المفاتيح أو الطرفية. وعندما تبدأ عملية جديدة، فإنها تبدأ بثلاثة "ملفات" مفتوحة تقابل stdin وstdout وstderr بواصفات الملفات 0 و1 و2 على الترتيب. وتحدّد قيم واصفات الملفات هذه أيًّا من stdin وstdout وstderr يُعاد توجيهه عند التنفيذ على سطر الأوامر:

  • `
  • > أو 1> يعيدان توجيه stdout
  • 2> يعيد توجيه stderr.

ويمكن دمج هذه المعاملات في أمر لإعادة توجيه واحد أو أكثر من stdin وstdout وstderr إلى ملف، مع الكتابة فوق محتواه. وتوفّر الصدفة أيضًا ترميزًا اختصاريًا لدمج إعادة توجيه stdout وstderr في ملف واحد، ولإلحاق الخرج بملف موجود:

  • &> يعيد توجيه stdin وstdout إلى الملف نفسه. لاحظ أن &>[outfile] يكافئ 1> [outfile] 2>&1، الذي يعيد توجيه stdout إلى outfile ويعيد توجيه stderr إلى stdout.
  • >> يلحق stdout بملف.

وفيما يلي بعض الأمثلة:

لإعادة توجيه stdout إلى ملفات أخرى باستخدام > أو 1> (نستخدم wc للتحقق من أن الملفين بالحجم نفسه، وdiff لرؤية أنهما متطابقان بعد استدعاء cat):

$ wc quote
  4  26 160 quote

$ cat quote
The function of education is to teach one
to think intensively and to think critically.
Intelligence plus character - that is the
goal of true education.

$ cat quote > another        # >: redirect cat's stdout to file "another"

$ wc another
  4  26 160 another

$ cat quote 1> yetanother    # 1>: redirect cat's stdout to a file (same as >)

$ diff -s another yetanother # check to see that both files are identical

لإعادة توجيه stdin من ملف باستخدام <:

$ wc < quote                # <: redirect wc's stdin from "quote" file
  4  26 160 quote

لإعادة توجيه stderr إلى ملف باستخدام 2>:

$ cat blah
cat: blah: No such file or directory

$ cat blah 2> error_out     # 2>: redirect stderr to a file "error_out"
$ wc error_out
 1  7 37 error_out

لإلحاق stdout الخاص بالبرنامج بملف باستخدام >>:

$ echo "Again:" >> another  # append echo's output to file "another"
$  wc another
  5  27 167 another

$ sort quote >> another     # append sort's output to file "another"

$ wc another
  9  53 327 another

$ cat another
The function of education is to teach one
to think intensively and to think critically.
Intelligence plus character - that is the
goal of true education.
Again:
Intelligence plus character - that is the
The function of education is to teach one
goal of true education.
to think intensively and to think critically.

لإعادة توجيه stdout وstderr معًا إلى الملف نفسه باستخدام &> (في هذا المثال لا يوجد الملف blah، لذا سيكتب cat رسالة خطأ إلى stderr، والملف quote موجود، لذا سيُخرج cat محتواه إلى stdout):

$ cat blah quote &> err_out

$ cat err_out
cat: blah: No such file or directory
The function of education is to teach one
to think intensively and to think critically.
Intelligence plus character - that is the
goal of true education.

لإعادة توجيه الثلاثة من ملفات مختلفة (في هذا المثال، لا يوجد خرج stderr، لذا ينبغي أن يكون ملف grep_error فارغًا بعد هذا الاستدعاء):

grep teach < another 1> grep_out 2> grep_error

$ cat grep_out
The function of education is to teach one
The function of education is to teach one

$ cat grep_err
$

17.12.1. المراجع

لمزيد من المعلومات:

تتيح الأنابيب للمستخدم ربط سلسلة من الأوامر في سطر أمر واحد. والأنبوب (|) يعيد توجيه stdout الخاص بالأمر المدرج قبل الأنبوب (|) إلى stdin الخاص بالأمر المدرج بعد الأنبوب:

$ cmd1 | cmd2            # cmd1's stdout is cmd2's stdin
$ cat quote
The function of education is to teach one
to think intensively and to think critically.
Intelligence plus character - that is the
goal of true education.

$ cat quote | grep th   # pipe stdout of cat into stdin of grep
                        # (find all lines containing "th")
to think intensively and to think critically.
Intelligence plus character - that is the

$ cat quote | wc        # pipe stdout of cat into stdin of wc
  4      26     160

$ ls
  another
  quote
$ ls | wc               # pipe ls output to wc
  2       2      14     # (number of lines, words, and chars in ls output)

ويمكن استخدام أنابيب متعددة في سطر الأمر نفسه لربط stdout في stdin لسلسلة من الأوامر:

cmd one | cmd two | ... | cmd n

وفيما يلي مثال على سطر أمر بأنبوبين. وفي هذا المثال، يُوجَّه stdout الخاص بـcat عبر أنبوب إلى stdin الخاص بـgrep th، ويُوجَّه stdout الخاص بـgrep th عبر أنبوب إلى إدخال wc:

$ cat quote | grep th | wc   # number of lines, words, chars in the
  2      14      93          # lines of quote file that contain "th"

وهذه مخرجات الأنبوب الأول في الأمر أعلاه، وهي تعرض خرج grep th الذي يُوجَّه عبر أنبوب إلى stdin الخاص بـwc في الأنبوب الثاني:

$ cat quote | grep th
to think intensively and to think critically.
Intelligence plus character - that is the

قد ترى أحيانًا الأمر xargs مستخدمًا في أسطر أوامر مع أنابيب. وينفّذ الأمر xargs الأمر cmd على كل قيمة تُمرَّر إليه على stdin. ولا نغطي xargs بالتفصيل، بل نوضّح ما يفعله مقارنةً بمثال ls | wc المعروض أعلاه.

يوضّح المثال أدناه الفرق بين wc وxargs wc كأمر بعد أنبوب (ls | wc يشغّل wc على خرج ls، بينما ls | xargs wc يشغّل wc على كل ملف يسرده ls):

$ ls
  another
  quote
$ ls | wc            # lines, words, chars in ls output
  2       2      14

$ ls | xargs wc      # lines, words, chars in each file listed by ls
  9  53 327 another
  4  26 160 quote
 13  79 487 total

17.13.1. المراجع

لمزيد من المعلومات انظر:

يوجد في نظام ملفات Unix عدد من ملفات النقطة وأدلة النقطة. وملفات النقطة ملفات يبدأ اسمها بمحرف .. وهذه الملفات مخفية عادةً لأن الأمر ls لا يسردها افتراضيًا. ولعرض هذه الملفات المخفية، استخدم وسيط سطر الأوامر -a مع ls. فمثلًا:

$ ls
unix_notes/

$ ls -a
  .   ..    .bashrc   .vimrc    .xsession  /unix_notes

يوجد دائمًا دليلا نقطة في كل دليل: . اسم مستعار للدليل نفسه، و.. اسم مستعار لدليل أبيه. وكما رأينا في القسم 17.1، كثيرًا ما يُستخدم . و.. في أسماء المسارات النسبية. فمثلًا، من الاستخدامات الشائعة لـ.. الصعود دليلًا واحدًا (لتغيير دليل العمل الحالي ليكون دليل أب الدليل الحالي):

$ cd ..

ويظهر المدخل . أيضًا كثيرًا في الأوامر التي تشغّل ملفًا تنفيذيًا من الدليل الحالي بسبق الأمر بـ./. فمثلًا، لتشغيل a.out في دليل العمل الحالي، سيكتب المستخدم ما يلي:

$ ./a.out

وكثير من ملفات النقطة ملفات تهيئة (أو أدلة ملفات تهيئة) لتطبيقات مختلفة. وتحتوي ملفات النقطة هذه خيارات خاصة بالتطبيق يمكنها تغيير سلوك التطبيق. ويقيّم برنامج محتوى ملف النقطة الخاص به عند أول بدء لتشغيله لتهيئة سلوكه. ويمكن للمستخدم تحرير كثير من ملفات النقطة هذه لتخصيص سلوك البرامج التي يشغّلها. وفيما يلي بعض الأمثلة على هذه الأنواع من ملفات النقطة:

  • .bashrc: ملف تهيئة لبرنامج صدفة bash، لضبط متغيرات البيئة وتعريف الأسماء المستعارة للأوامر…​
  • .vimrc: تهيئة لمحرر vim، لتعريف نظام الألوان والإبراز والترميز…​
  • .xsession: # ملف تهيئة لمديري X Windows الذين يبدأ تشغيلهم بعد تسجيل الدخول في طرفية. لضبط لون الخلفية وتعريف بعض السلوك الافتراضي وبدء بعض التطبيقات وبدء مدير نوافذ معيّن (وله ملفات تهيئة نقطية خاصة به)

17.14.1. ملف .bashrc

تحتوي ملفات تهيئة صدفة Unix (مثل .bashrc لصدفة Bash) مدخلات تغيّر سلوك الصدفة. وعادةً يعدّ مدير النظام ملف .bashrc افتراضيًا للنظام. غير أن المستخدمين الأفراد غالبًا ما يريدون تخصيص صدفة لهم أكثر، ويمكنهم فعل ذلك بتحرير .bashrc في دليلهم الرئيسي.

ملاحظة

تقرأ الصدفة ملف .bashrc عند أول بدء لتشغيلها. ولذلك لا تسري التغييرات على ملف .bashrc إلا إذا شغّل المستخدم source .bashrc أو أعاد تشغيل bash.

ومن التغييرين الشائعين اللذين يجريهما المستخدمون تغيير تعريف متغيرات بيئة الصدفة وإضافة أسماء مستعارة للأوامر (اختصارات المستخدم للأوامر التي يشغّلها كثيرًا). وتُستخدم متغيرات بيئة الصدفة لتحدّد الصدفة كيفية عمل بعض ميزات الصدفة. ولسرد جميع متغيرات البيئة، استخدم الأمر env، أو اسرد قيمة متغير بيئة معيّن باستخدام echo $env_var_name. فمثلًا:

$ echo $GROUP    # list your default unix file group
users

$env             # list value of all env vars (a lot)
SHELL=/bin/bash
GROUP=users
...
PATH=/usr/bin:/usr/local/bin:/bin
...

ومتغير البيئة PATH مثير للاهتمام بشكل خاص---فهو يسرد مجموعة الأدلة التي تبحث فيها الصدفة (بالترتيب) عن البرامج عندما يُدخل المستخدم أمرًا عند مطالبة الصدفة. فمثلًا، بالنظر إلى تعريف PATH المعروض أعلاه، إذا أدخل مستخدم الأمر ls، فستبحث الصدفة أولًا عنه في دليل /usr/bin (تبحث الصدفة عن ملف تنفيذي اسمه ls) وتشغّله إن وجدته. وإذا لم يُوجد ls في /usr/bin، فستبحث الصدفة تاليًا في /usr/local/bin، وإن لم يُوجد بحثت تاليًا في /bin، وإن لم يُوجد بعد ذلك طبعت الصدفة رسالة خطأ تقول إن الأمر ls غير موجود. ومتغير البيئة PATH هو السبب في أن المستخدمين لا يحتاجون إلى سرد اسم المسار الكامل للأوامر الشائعة، مثل ls، عند تشغيلها!

ولأنه قد توجد نسخ متعددة من البرنامج نفسه في مواقع مختلفة، فإن ترتيب أسماء الأدلة في $PATH يحدّد أيّها يُشغَّل. ويمكن للمستخدم رؤية أي نسخة من برنامج يشغّلها باستخدام الأمر which. فمثلًا:

$ which ls
/usr/bin/ls

ويمكن للمستخدمين أيضًا إعادة تعريف PATH في ملفات .bashrc لديهم لتغيير ترتيب المسارات التي تبحث فيها Bash أو لإضافة مسارات بحث إضافية. فمثلًا، إذا أنشأت Sarita برامج أدوات خاصة بها تشغّلها كثيرًا، فقد تريد وضعها في دليل اسمه mybin في دليلها الرئيسي ثم تحديث متغير بيئة PATH ليشمل هذا الدليل لتشغيلها دون سرد اسم مسارها الكامل في كل مرة. ويمكنها فعل ذلك بتعديل التعريف الموجود لـPATH في ملف bashrc لديها أو بإضافة تعريف إضافي لـPATH كالتالي:

PATH=$PATH:/home/sarita/mybin

ويضيف هذا التعريف دليلها mybin إلى نهاية متغير بيئة PATH الموجود لديها (يضبط PATH على القيمة الحالية لـPATH مع مسار آخر اسمه /home/sarita/mybin).

وعندما تبدأ Sarita صدفة Bash جديدة، أو إذا كتبت source ~/.bashrc في صدفة Bash قيد التشغيل، سيُحدَّث مسارها ليشمل /home/sarita/mybin/.

ومن التعديلات الشائعة الأخرى على ملف .bashrc إضافة أسماء مستعارة مختصرة لأوامر تُكتب كثيرًا أو لأوامر تضمن تشغيلها دائمًا بخيارات سطر أوامر معيّنة. ومن الأمثلة الشائعة جعل الأمر rm -i اسمًا مستعارًا لـrm. وهذا يضمن أنه كلما أُدخل الأمر rm على سطر الأوامر، شغّلت الصدفة فعليًا rm -i، الذي يطلب من المستخدم تأكيد رغبته الحقيقية في حذف الملف قبل إزالته. وهكذا تضيف هذا الاسم المستعار لـrm في ملف .bashrc:

alias rm="rm -i"      # add alias for rm to double check

وبالمثل، إذا كان Sam يشغّل الأمر cd ~/classes/CS31/ كثيرًا، فقد يريد إضافة اسم مستعار مختصر في Bash لهذا الأمر في ملف .bashrc لديه هكذا:

alias gt31="cd ~/classes/CS31"  # goto (cd) into my CS31 directory

ثم يمكن لـSam البدء باستخدام هذين الاسمين المستعارين الجديدين. فمثلًا:

$ source ~/.bashrc    # source: make bash re-evaluate .bashrc file

$ rm notes            # rm is now an alias for rm -i
rm: remove regular file 'notes'? n
$ ls
classes/  letters/  notes  projects/

$ rm notes
rm: remove regular file 'notes'? y
$ ls
classes/  letters/  projects/

$ gt31
$ pwd
/home/sam/classes/CS31

17.14.2. المراجع

لمزيد من المعلومات انظر:

غالبًا ما تُستخدم سكربتات الصدفة لتجميع مجموعة من الأوامر التي ينبغي أن تُنفَّذ معًا. وسكربت الصدفة (shell script) ملف نصي تنفيذي يتكوّن من تتابع من أوامر الصدفة التي تنفّذ تلك الأوامر بالترتيب عند تشغيلها. وللصدفات أيضًا تراكيب لغوية مثل الحلقات والتعبيرات الشرطية يمكن استخدامها لإنشاء سكربتات معقّدة غنية بالميزات. ويمكن استخدام تراكيب البرمجة الصدفية هذه أيضًا على سطر الأوامر وفي سكربتات الصدفة. وفيما يلي نعرض بعض الأمثلة على البرمجة بصدفة Bash. وللصدفات الأخرى (مثل zsh وsh وtcsh) صياغة لغة برمجة صدفية خاصة بها، لكنها توفّر جميعًا وظائف مشابهة.

يبدأ السطر الأول من سكربت Bash بتعليق خاص # متبوعًا بعلامة تعجّب ! ومسار البرنامج الذي ينفّذ محتويات ملف السكربت. وفيما يلي مثال على سكربت Bash بسيط، simplescript.sh (لاحظ أن سكربتات الصدفة تُسمّى حسب الاصطلاح بلاحقة .sh).

#!/bin/bash
pwd
whoami
date

وقبل أن يمكن تشغيل ملف simplescript.sh، يجب منحه صلاحية التنفيذ. ويمكن للمستخدم التحقق من صلاحياته بتشغيل ls -l وضبط صلاحية التنفيذ باستخدام chmod إذا لم تكن مضبوطة. فمثلًا:

$ ls -l simplescript.sh
-rw------- 1 sam users 31 Mar 28 18:26 simplescript.sh

$ chmod u+x simplescript.sh

$ ls -l simplescript.sh
-rwx------ 1 sam users 31 Mar 28 18:26 simplescript.sh

وبعد تمييزه كملف تنفيذي، يمكن تشغيل السكربت على سطر الأوامر كأي ملف تنفيذي، وسينفّذ تسلسل الأوامر المدرج في السكربت بالترتيب:

$ ./simplescript.sh
/home/sam/
sam
Tue 28 Mar 2023 06:27:38 PM EDT

وتدعم برامج صدفة Unix تراكيب لغوية مثل التكرار (أي حلقات for وwhile) والتعبيرات الشرطية (أي if-else). ومع أنه يمكن استخدام هذه التراكيب على سطر الأوامر، فإنها تُستخدم أكثر في سكربتات الصدفة لتحديد بنية تنفيذ أكثر تعقيدًا. ويمكن لسكربتات الصدفة أيضًا استخدام وسائط سطر الأوامر والمتغيرات، وهي تساعد السكربت على العمل بقيم إدخال مختلفة.

وفيما يلي مثالان على سكربتات صدفة Bash تستخدمان بعض هذه الميزات الأكثر تقدمًا. الأول، runsh.sh، سكربت يعيد بعض التشغيلات الموقوتة لبرنامج. وفي هذا المثال، يُعطى اسم الملف التنفيذي وعدد التشغيلات المراد تكرارها كوسائط سطر أوامر للسكربت. كما يوضّح هذا المثال كيفية اختبار بعض القيود على وسائط سطر الأوامر. وهنا يكون وسيط سطر الأوامر الخاص بالبرنامج التنفيذي مطلوبًا لتشغيل السكربت، لكن عدد التشغيلات المراد تكرارها وسيط سطر أوامر اختياري (يستخدم السكربت قيمة افتراضية مقدارها 5 عندما يشغّل المستخدم السكربت دون تحديد خيار سطر الأوامر هذا). ويستخدم السكربت التركيب if-elif لاختبار خيارات سطر الأوامر. كما يعرض مثالًا على حلقة for تُستخدم لتكرار عدد التشغيلات الذي يحدّده المستدعي.

#!/bin/bash

# this script performs a number of timed runs of a
# program given as a required command line argument
# the number of runs is an optional cmdln arg

# script variable N for the number of runs
# assigning N default value of 5
N=5

# get command line arguments
#  1: name of program is required
#  2: number of runs is optional
# $@ is the array of command line args (like argv)
# $# is the number of command line args (like argc)

# get the program executable name
# note: space between "[ $" and between "0 ]" is important!
if [[ $# -eq 0 ]]
then
  echo "Error, usage: ./run.sh ./a.out [num times]"
  exit 1
else
  PROG=$1
fi

# if they gave the optional cmdln arg, set N to it
if [[ $# -gt 1 ]]
then
  N=$2
fi

# it's useful to output some info about what is run:
echo "running $PROG $N times: "
date
echo "======================= "

# do N timed runs of PROG:
for((n=1; n <= $N; n++))
do
echo " "
echo "run $n:"
time $PROG
done

وهناك أمور قليلة جديرة بالملاحظة في هذا السكربت. أولًا، لا توجد أنواع في سكربتات Bash، لذا يعود إلى المستخدم استخدام القيم استخدامًا مناسبًا بناءً على أنواعها الضمنية.

ثانيًا، لاحظ أن متغيرات صدفة Bash تُضبط بالمعامل = وتُشار إلى قيمها بـ$var_name:

N=5      # set N to 5
n <= $N  # use value of N

ثالثًا، لاحظ صياغة الوصول إلى وسائط سطر الأوامر في سكربت Bash:

  • $@ هي مصفوفة وسائط سطر الأوامر، وهي مشابهة لـargv في C.
  • $# هو عدد وسائط سطر الأوامر، وهو مشابه لـargc في C إلا أن اسم ملف سكربت Bash لا يُحتسب واحدًا من الوسائط (أي أن سكربت Bash مشغّلًا بلا وسائط سطر أوامر تكون قيمة $# لديه 0، بينما برنامج C مشغّل بلا وسائط سطر أوامر تكون قيمة argc لديه 1).

رابعًا، لاحظ صياغة if-else وصيغتها العامة:

if [[ cond ]]
then
   # if true stmts
else
   # if false stmts
fi

تستخدم Bash الكلمات then وelse وfi للدلالة على بداية كتلة if وبداية كتلة else ونهاية عبارة if-else. ويُستخدم الرمزان [[ و]] للدلالة على العبارة الشرطية، ومحارف المسافة بينهما وبين الشرط مهمة جدًا (فبدونها لن تفسّر Bash هذه العبارة تفسيرًا صحيحًا). وصياغة المعاملات الشرطية هي: -gt تمثّل أكبر من، و-lt تمثّل أصغر من، و-eq تمثّل يساوي، وغيرها يستخدم ترميزًا مشابهًا.

وأخيرًا، لاحظ أن صياغة حلقة for مشابهة لحلقات for في C، حيث يُرمز إلى جسم حلقة for بالكلمتين do وdone ويحتوي جزأي init; cond; step داخل قوسين مزدوجين. وهذه هي الصيغة العامة لهذا النوع من حلقات for في Bash (ولـBash في الواقع أكثر من صيغة واحدة لحلقة for):

for ((init; cond; step))
do
  # for loop body statements
done

وفيما يلي بعض التشغيلات النموذجية لسكربت run.sh:

$ ./run.sh
Error, usage: ./run.sh ./a.out [num times]

$ ./run.sh ./myprog 2
running ./myprog 2 times:
Wed Mar 29 11:34:34 AM EDT 2023
=======================

run 1:
myprog result = 300000

real	0m0.060s
user	0m0.003s
sys	0m0.006s

run 2:
myprog result = 300000

real	0m0.059s
user	0m0.003s
sys	0m0.006s

$ ./run.sh ./myprog
running ./myprog 5 times:
Wed Mar 29 11:34:37 AM EDT 2023
=======================

run 1:
myprog result = 300000

real	0m0.057s
user	0m0.008s
sys	0m0.000s

run 2:
myprog result = 300000

real	0m0.057s
user	0m0.004s
sys	0m0.004s

run 3:
myprog result = 300000

real	0m0.057s
user	0m0.010s
sys	0m0.000s

run 4:
myprog result = 300000

real	0m0.056s
user	0m0.009s
sys	0m0.000s

run 5:
myprog result = 300000

real	0m0.055s
user	0m0.004s
sys	0m0.004s

وغالبًا عند استخدام سكربتات مثل run.sh تنتج مخرجات كثيرة، يكون من المفيد حفظ المخرجات في ملف يمكن فحصه بعد الاكتمال. وإحدى طرق فعل ذلك استخدام إعادة توجيه الإدخال/الإخراج لإعادة توجيه خرج السكربت إلى ملف. وفيما يلي مثال على تشغيل سكربت run.sh بخياري سطر الأوامر ./myprog و10 مع إعادة توجيه خرجه من stderr وstdout إلى ملف اسمه results:

$ ./run.sh ./myprog 10 &> results

وإلى جانب دعم صياغة مشابهة لحلقة for في C، تدعم Bash صياغة حلقة for للتكرار عبر مجموعة من العناصر. ويحتوي سكربت Bash المسمّى forloops.sh (المدرج أدناه) بعض الأمثلة على هذا النوع من حلقات for، مع عرض طرق مختلفة للحصول على مجموعة العناصر المراد التكرار عبرها.

#!/bin/bash

echo "for loop over set of values"
# iterate over a set of given values
# repeats once for each element in the list
for i in cat dog bunny
  do
    echo "next animal is: $i"
  done

echo

echo "for loop over sequence"
# {1..5} is the set of values in the sequence: 1,2,3,4,5
# this iterates over "infilei" where i: 1,2,3,4,5
for i in infile{1..5}
  do
    echo "next file is: $i"
  done

echo

echo "for loop over set created with ls"
# this iterates over all the files in a subdirectory named input
# $(ls input/): creates a set of value that are all the file
# and directory names in the input/ subdirectory
for i in $(ls input/)
  do
    echo "next input/ file is: $i"
  done

وعند التشغيل، تكون المخرجات:

$ ./forloops.sh

for loop over set of values
next animal is: cat
next animal is: dog
next animal is: bunny

for loop over sequence
next file is: infile1
next file is: infile2
next file is: infile3
next file is: infile4
next file is: infile5

for loop over set created with ls
next input/ file is: albums
next input/ file is: artists
next input/ file is: bands
next input/ file is: songs

وبشكل عام، فإن دمج أوامر Unix مع تراكيب لغة صدفة Bash مثل الحلقات والتعبيرات الشرطية وأسطر الأوامر والمتغيرات يتيح للمستخدم كتابة سكربتات صدفة قوية لتنفيذ إجراءات معقّدة. وقد عرضنا أمثلة قليلة فقط في هذا القسم. انظر دليل برمجة صدفة Bash لمزيد من المعلومات.

17.15.1. المراجع

لمزيد من المعلومات انظر:

توجد أدوات كثيرة للحصول على معلومات عن حالة النظام: يوفّر top وhtop معلومات ملخّصة ديناميكية عن موارد النظام واستخدامها، مثل استخدام CPU والذاكرة؛ ويحتوي نظاما الملفات الزائفان /proc و/sys معلومات مفصّلة عن الحالة الحالية للنظام (وتوجد أيضًا أوامر مثل lscpu وlsmem وlsgpu تعرض بعض المعلومات من /proc و/sys بصيغة أسهل قراءة).

17.16.1. top وhtop

يوفر تشغيل top أو htop على سطر الأوامر معلومات ملخّصة ديناميكية عن حالة النظام. وكتابة q تخرج من top وhtop. ويعرض كلاهما معلومات استخدام الذاكرة وCPU، لكن htop يعرض بعض إحصاءات الاستخدام بشكل أكثر رسمية من top، وخصوصًا عرض استخدام الذاكرة واستخدام CPU لكل نواة كرسم بياني. كما يسرد كلاهما افتراضيًا معلومات عن المهام التي تستخدم أكبر وقت CPU حاليًا. والمهمة (task) هو المصطلح العام في Linux لعملية أو خيط. وتُحدَّث هذه الأدوات كل ثانية أثناء عملها، فتعرض التغييرات في بيانات استخدام النظام الملخّصة. لاحظ أنه مع تحديث العرض كل ثانية، سيتغير ترتيب العمليات أثناء تنفيذها حسب مقدار وقت CPU الذي تستخدمه. وكلا الأداتين قابل للتهيئة بدرجة كبيرة لعرض معلومات مختلفة عن العمليات وحالة النظام.

ويعرض الشكل 1 لقطة شاشة نموذجية من htop.

مخرجات نموذجية من htop تُظهر الاستخدام الحالي للنوى الـ12 من المعالج، واستخدام الذاكرة، ومعلومات عن العمليات الأعلى استهلاكاً للمعالج. الشكل 1. لقطة شاشة نموذجية من htop).

وفي أعلى الشاشة تمامًا، يعرض htop استخدام CPU الحالي لكل نواة من النوى الـ12 في هذا النظام كرسم بياني للاستخدام مُعلَّم بالنسب المئوية، يليه استخدام الذاكرة الحالي (Mem)، ومعلومات ملخّصة عن متوسط حِمل CPU (Load average)، والعدد الإجمالي للمهام (Tasks) والخيوط (thr) العاملة في النظام، ومعلومات عن استخدام قسم المبادلة (Swp). وتحتها سرد للمهام التي تستخدم أكبر وقت CPU في النظام. لاحظ أن المهام الخمس الأولى المدرجة في الشكل 1 عمليات تشغّل برنامج الحلقة اللانهائية a.out، لذا فهي تستهلك وقت CPU كثيرًا كما هو متوقع. وفي أسفل الشاشة مفاتيح وظائف لتغيير البيانات المعروضة بطرق مختلفة.

ويعرض برنامج top بيانات شبيهة جدًا بـhtop (لكن دون العروض الرسومية في الأعلى)، ويوفّر بالمثل واجهة لتغيير البيانات المعروضة بطرق مختلفة أثناء عمله. فمثلًا، يمكن للمستخدمين رؤية استخدام CPU لكل نواة في top بضغط مفتاح "1".

وإلى جانب كونهما قابلين للتهيئة بدرجة كبيرة عند التشغيل، فإن لـtop وhtop عددًا كبيرًا من خيارات سطر الأوامر لتهيئة ما يعرضانه وكيفية عرضه. فمثلًا، لا يعرض top افتراضيًا إحصاءات لكل خيط (بل لكل عملية فقط)، لكنه سيفعل ذلك عند تشغيله بخيار سطر الأوامر -H.

17.16.2. ‏/proc و/sys

/proc و/sys نظاما ملفات زائفان يوفّران الوصول إلى معلومات نظام التشغيل عن العمليات العاملة في النظام وإلى معلومات عن حالة النظام وموارده (مثل الأجهزة والنواقل والذاكرة والمعالج). ونظام الملفات الزائف يشبه نظام ملفات ويمكن التفاعل معه عبر واجهة نظام الملفات، لكنه لا يخزّن بيانات ملفات فعلًا. وبدلًا من ذلك، يمكن للمستخدم عبر ملفات زائفة في /proc و/sys التفاعل مع نظام التشغيل؛ فالقراءة من ملفات زائفة في /proc و/sys (مثل cat /proc/filename) طريقة للحصول على معلومات عن حالة النظام من نظام التشغيل. ويمكن أيضًا الكتابة في بعض الملفات في /proc و/sys، وقد يحفّز ذلك نظام التشغيل على تغيير سياسة أو تغيير الحالة المرتبطة بعملية أو مورد معيّن في النظام. فمثلًا، قد يكتب مصحّح أخطاء مثل GDB في ملف /proc مرتبط بالعملية التي يصحّحها لضبط نقطة توقف، أو قد يقرأ من ملف /proc لفحص المحتوى الحالي لذاكرة مكدّسها.

وفيما يلي بعض الأمثلة على نوع المعلومات التي يمكن الحصول عليها بالقراءة من ملفات في /proc. وفي قسمنا عن التخزين المؤقت على المعالجات متعددة الأنوية نعرض بعض الأمثلة على كيفية الحصول على معلومات عن ذاكرات CPU المؤقتة من ملفات في /sys.

ولكل عملية عاملة في النظام دليل فرعي في /proc يحمل اسمه معرّف عمليتها ويحتوي كثيرًا من الملفات الزائفة التي هي واجهات لحالة العملية ووضعها. فمثلًا، لنفترض أننا نريد الحصول على بعض المعلومات من /proc عن عملية a.out العاملة في الخلفية هكذا:

$ ./a.out &

نحصل أولًا على معرّف عمليتها بتشغيل الأمر ps:

$ ps
    PID TTY          TIME CMD
 246477 pts/0    00:00:00 bash
 246541 pts/0    00:01:59 a.out
 246879 pts/0    00:00:00 ps

ثم يمكننا استخدام أوامر نظام الملفات للحصول على معلومات عن برنامج a.out العامل باستخدام معرّف عمليته 246541 (ولاحظ أنه لا يمكنك الوصول إلا إلى الأدلة الفرعية في /proc المرتبطة بعمليات تملكها). فمثلًا:

$ ls /proc/246541/         # list all pseudo files for this process

$ cat /proc/246541/comm    # list its executable file name
$ cat /proc/246541/status  # list its status information

ويسرد الملف الزائف status معلومات كثيرة، منها ما يلي:

State: R (running)  # its state: running, blocked, exited, ...
VmStk: 132 kB       # size of its stack (in its virtual memory (Vm))
nonvoluntary_ctxt_switches:  59593   # context switches due to OS CPU scheduling

برنامج a.out المثال هذا يحتوي حلقة لا نهائية؛ وبينما يواصل العمل، يزداد عدد تبديلات السياق، وستكون حالته دائمًا R (أي قيد التشغيل)، حتى يُنهى بـCTRL-C.

ويمكن أيضًا استخدام الملفات الزائفة في /proc (وفي /sys) للحصول على معلومات من نظام التشغيل عن موارد النظام عمومًا. وفيما يلي بعض الأمثلة على الحصول على معلومات من /proc (وفي فصل هرمية الذاكرة نعرض مثالًا على الحصول على التخزين المؤقت على المعالجات متعددة الأنوية من /sys):

cat /proc/meminfo    # info about system memory and its current use
cat /proc/cpuinfo    # info about each hyperthreaded core
cat /proc/swaps      # info about swap partitions and their use
cat /proc/stat       # lots of system statistics

ويوفّر Linux أدوات تلخّص بعض المعلومات في /proc و/sys بصيغة غالبًا ما تكون أسهل قراءة من المخرجات الناتجة عن القراءة المباشرة من ملفات /proc و/sys. ويُعدّ lshw وlscpu وlsgpu أمثلة على أوامر تسرد معلومات عن عتاد النظام وCPU وأجهزة GPU في الجهاز.

فمثلًا، لمعرفة عدد الأنوية الفيزيائية في جهاز ما يمكن للمستخدم تشغيل lscpu:

CPU(s):                          32
On-line CPU(s) list:             0-31
Thread(s) per core:              2
Core(s) per socket:              8
Socket(s):                       2

وتُظهر المخرجات أن في الجهاز 16 نواة فيزيائية إجمالًا (مقبسان/شريحتان، و8 أنوية لكل مقبس/شريحة). كما تُظهر أن كل نواة ثنائية الخيوط الفائقة (2 Thread(s) per core)، لجعل الجهاز ذي 16 نواة يبدو لنظام التشغيل جهازًا ذا 32 نواة. ويرجى ملاحظة أن معماريات Intel الأحدث تُدخل أنوية P وأنوية E. وفي هذه المعماريات، قد يكون العدد الإجمالي لـCPU المسرودة أعلى من عدد الأنوية الفيزيائية مضروبًا في عدد المقابس والخيوط العتادية. انظر الفصل 14 لمزيد من المعلومات.

17.16.3. المراجع

لمزيد من المعلومات انظر: