البرمجة

برمجة سطر الأوامر بـRust

برنامج سطر الأوامر Command Line بلغة Rust: التعامل مع الدخل والخرج

تُعدّ لغة Rust واحدة من اللغات البرمجية الحديثة التي حققت شهرة واسعة بفضل أدائها العالي، أمانها في التعامل مع الذاكرة، وقدرتها على إنشاء برامج متينة وقابلة للصيانة. من بين التطبيقات العملية الشائعة للغة Rust هو كتابة برامج تعمل على سطر الأوامر (Command Line Interface – CLI)، التي تعتمد على التعامل مع الدخل (Input) والخرج (Output) من خلال واجهة نصية. في هذا المقال، سنغوص في تفاصيل كيفية كتابة برنامج سطر أوامر باستخدام Rust مع التركيز على آليات التعامل مع الدخل والخرج.


مقدمة إلى برمجة سطر الأوامر بلغة Rust

برامج سطر الأوامر هي برامج تُشغل من خلال واجهة النصوص، مثل Terminal في أنظمة Linux وmacOS أو Command Prompt وPowerShell في Windows. هذه البرامج تعتمد بشكل أساسي على قراءة بيانات من المستخدم عبر سطر الأوامر، معالجة هذه البيانات، ثم إظهار النتائج أو إرسالها إلى ملفات أو عمليات أخرى.

تتميز Rust بأنها توفر مكتبات وأدوات متطورة تسهل بناء هذه البرامج مع الحفاظ على السرعة والأمان، مثل مكتبة std::io التي تدير عمليات الدخل والخرج، إضافة إلى مكتبات خارجية مثل clap التي تسهل معالجة الوسائط Arguments الخاصة بسطر الأوامر.


مفاهيم أساسية في التعامل مع الدخل والخرج في Rust

1. الدخل Input

مصادر الدخل الأساسية في برامج CLI هي:

  • المدخل القياسي (Standard Input): بيانات تُدخَل من لوحة المفاتيح أو من ملفات تُحول إلى البرنامج عبر الأنابيب (Pipes).

  • وسائط سطر الأوامر (Command-line Arguments): المعطيات التي يمررها المستخدم عند تشغيل البرنامج، مثل أسماء ملفات أو خيارات.

2. الخرج Output

مصادر الخرج الأساسية:

  • المخرج القياسي (Standard Output): النصوص أو البيانات التي يطبعها البرنامج لتظهر في واجهة المستخدم.

  • المخرج الخطأ (Standard Error): تستخدم لإظهار رسائل الخطأ والتنبيهات بشكل منفصل عن المخرجات العادية.

  • الكتابة إلى ملفات: حفظ البيانات في ملفات على القرص.


آليات الدخل والخرج في مكتبة std::io

تقدم مكتبة Rust القياسية std::io عدة أدوات للتعامل مع الدخل والخرج:

  • القراءة من stdin: عن طريق std::io::stdin() يمكن قراءة مدخلات المستخدم.

  • الكتابة إلى stdout: باستخدام println! للطباعة السريعة أو std::io::stdout() للكتابة المنسقة.

  • الكتابة إلى stderr: باستخدام eprintln! للطباعة إلى مخرج الأخطاء.


مثال عملي: قراءة نص من المستخدم وطباعة استجابة

لنبدأ بمثال بسيط يوضح قراءة نص من المستخدم ثم طباعته:

rust
use std::io::{self, Write}; fn main() { // إنشاء متغير لتخزين المدخلات let mut input = String::new(); print!("أدخل نصاً: "); io::stdout().flush().unwrap(); // تأكيد طباعة النص قبل الانتظار للمدخلات io::stdin() .read_line(&mut input) .expect("فشل في قراءة السطر"); println!("لقد أدخلت: {}", input.trim()); }

شرح الكود:

  • let mut input = String::new(); ينشئ متغير قابل للتغيير لتخزين النص المدخل.

  • print! لطباعة النص بدون إضافة سطر جديد، ولذلك نستخدم io::stdout().flush() للتأكد من أن النص يظهر قبل انتظار المستخدم.

  • io::stdin().read_line(&mut input) تقرأ سطر من الدخل القياسي وتحفظه في المتغير.

  • input.trim() لإزالة المساحات البيضاء الزائدة مثل نهاية السطر.


قراءة وسائط سطر الأوامر (Command-line Arguments)

تعتبر وسائط سطر الأوامر وسيلة فعالة لإعطاء البرامج مدخلات من خارجها. في Rust، يمكن الحصول عليها عبر std::env::args().

مثال بسيط:

rust
use std::env; fn main() { let args: Vec<String> = env::args().collect(); println!("عدد الوسائط: {}", args.len()); for (index, argument) in args.iter().enumerate() { println!("وسيط {} = {}", index, argument); } }

شرح الكود:

  • env::args() ترجع تكرار (Iterator) على الوسائط.

  • collect() تجمع الوسائط في مصفوفة.

  • args[0] هو عادة اسم البرنامج نفسه.

  • نطبع عدد الوسائط ونتجول عليها لعرضها.


التعامل مع ملفات الدخل والخرج

إضافة إلى التعامل مع stdin/stdout، كثيراً ما تحتاج برامج CLI إلى قراءة وكتابة ملفات. توفر Rust أدوات قوية في std::fs للقيام بذلك.

قراءة ملف نصي

rust
use std::fs::File; use std::io::{self, BufRead, BufReader}; fn main() -> io::Result<()> { let file = File::open("input.txt")?; let reader = BufReader::new(file); for line in reader.lines() { let line = line?; println!("{}", line); } Ok(()) }

كتابة إلى ملف نصي

rust
use std::fs::File; use std::io::{self, Write}; fn main() -> io::Result<()> { let mut file = File::create("output.txt")?; file.write_all(b"مرحباً بكم في Rust!")?; Ok(()) }

مكتبة clap: تسهيل التعامل مع الوسائط

بدلاً من التعامل اليدوي مع الوسائط، تقدم مكتبة clap واجهة مرنة لبناء برامج CLI متقدمة. تدعم تعريف خيارات، أعلام Flags، أوامر فرعية Subcommands، وغيرها.

مثال على استخدام clap:

rust
use clap::{Arg, Command}; fn main() { let matches = Command::new("مثال برنامج") .version("1.0") .author("المؤلف") .about("مثال على برنامج CLI") .arg( Arg::new("config") .short('c') .long("config") .value_name("FILE") .help("يحدد ملف الإعدادات") .takes_value(true), ) .get_matches(); if let Some(config) = matches.value_of("config") { println!("ملف الإعدادات: {}", config); } else { println!("لم يتم تحديد ملف إعدادات"); } }

التعامل مع الأخطاء في برامج CLI

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

Rust توفر آلية التحكم بالخطأ عبر نوع Result، ويجب التعامل معه إما عن طريق التعامل المباشر مع الخطأ أو إعادة تمريره. استخدام expect() أو unwrap() مناسب في حالات التطوير، أما في البرامج النهائية يفضل التعامل مع الخطأ بشكل أكثر مرونة.


تحسينات وتجارب عملية

الكتابة بشكل غير متزامن Asynchronous I/O

Rust تدعم الكتابة والقراءة غير المتزامنة عبر مكتبة tokio، ما يتيح بناء برامج CLI أسرع عند التعامل مع مدخلات وملفات ضخمة.

التنسيق والتلوين في مخرجات CLI

يمكن تحسين تجربة المستخدم بإضافة ألوان أو تنسيقات للنصوص عبر مكتبات مثل colored أو termcolor، ما يجعل مخرجات البرنامج أكثر وضوحاً وجاذبية.


مقارنة بين طرق مختلفة لقراءة الدخل في Rust

الطريقة الوصف الاستخدام الأمثل المزايا العيوب
stdin().read_line() قراءة سطر كامل من مدخل المستخدم البرامج التي تحتاج لقراءة نص واحد بسيطة وسهلة الاستخدام غير مناسبة للمدخلات الكبيرة جداً
env::args() قراءة وسائط سطر الأوامر البرامج التي تعتمد على خيارات ومدخلات من السطر سهلة التعامل مع الوسائط لا تقرأ من المستخدم بعد بدء البرنامج
BufReader + ملف قراءة ملفات نصية بشكل فعال البرامج التي تتعامل مع ملفات فعالة من حيث الأداء تحتاج إلى مزيد من الكود

خاتمة

تتمتع لغة Rust بإمكانات مميزة لبناء برامج سطر أوامر قوية ومتينة بفضل مكتباتها القياسية والأنظمة التي تقدمها للتعامل مع الدخل والخرج. سواء كان الهدف هو بناء برنامج بسيط يقرأ من المستخدم ويطبع نصوصاً، أو برنامج متكامل يدير وسائط متعددة ويكتب في ملفات، توفر Rust الأدوات المناسبة مع ضمان الأداء والأمان. كما أن المكتبات الخارجية مثل clap توسع إمكانيات البناء وتجعل البرامج أكثر احترافية وسهولة في الاستخدام.

العمل مع الدخل والخرج في Rust يتطلب فهم آليات إدارة الذاكرة، الأنواع، والتعامل مع الأخطاء بشكل صحيح، ما يعزز من جودة البرنامج ويقلل من الأعطال. هذه الميزات جعلت Rust خيارًا متقدمًا لتطوير برامج CLI في مختلف المجالات.


المصادر والمراجع