تنفيذ الأوامر في Rust: كيفية التقاط المخرجات في 2025

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

من Python إلى Rust: تطور تنفيذ الأوامر

\n\n

إذا كنت قد تساءلت يومًا "How do I invoke a system command and capture its output?" على Stack Overflow، فأنت تعلم: هذا أحد أكثر الأسئلة شيوعًا وحيوية لأي مطور. في عالم Python، الإجابة بسيطة — subprocess.check_output(). ولكن ماذا تفعل إذا انتقلت إلى Rust وتريد ليس فقط تشغيل أمر، بل القيام بذلك بأناقة وأمان وبأقصى أداء؟

\n\n

Rust ليست مجرد "لغة أخرى". إنها نظام حيث كل بايت تحت السيطرة، وأخطاء تنفيذ الأوامر لا تؤدي إلى انهيار التطبيق بأكمله. في عام 2025، عندما أصبح الأمان والسرعة معيارًا واقعيًا، فإن القدرة على العمل بشكل صحيح مع العمليات الفرعية في Rust هي مهارة لا غنى عنها. دعنا نستعرض أفضل الممارسات التي تحول تشغيل أوامر النظام من روتين إلى فن.

\n\n

std::process::Command: سكينك السويسري

\n\n

قلب تنفيذ الأوامر في Rust هو الوحدة std::process::Command. توفر كل ما هو ضروري لتشغيل البرامج الخارجية، والتقاط مخرجاتها، ومعالجة الأخطاء. على عكس Python، حيث يخفي subprocess العديد من التفاصيل، يمنحك Rust السيطرة الكاملة.

\n\n

مثال أساسي: التقاط stdout

\n\n

لنبدأ بالكلاسيكيات — تنفيذ الأمر echo "Hello, Rust!" والتقاط مخرجاته:

\n\n
use std::process::Command;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let output = Command::new("echo")\n        .arg("Hello, Rust!")\n        .output()?;\n\n    let stdout = String::from_utf8(output.stdout)?;\n    println!("مخرجات الأمر: {}", stdout);\n\n    Ok(())\n}\n
\n\n

ما المهم هنا؟ نستخدم .output() الذي يعيد بنية Output. تحتوي على ثلاثة حقول: stdout و stderr و status. لاحظ ? — هذا هو عامل نشر الأخطاء الذي يعفينا من كتل match الضخمة. تجبرنا Rust على معالجة الأعطال المحتملة بشكل صريح: قد لا يكون الأمر موجودًا، أو لا يملك صلاحيات الوصول، أو ببساطة ينتهي بخطأ.

\n\n

معالجة stderr ورمز الإرجاع

\n\n

في السيناريوهات الواقعية، لا تحتاج فقط إلى stdout بل أيضًا إلى stderr. تخيل أنك تشغل ls /nonexistent — أمر سيؤدي بالتأكيد إلى خطأ:

\n\n
use std::process::Command;\n\nfn main() {\n    let output = Command::new("ls")\n        .arg("/nonexistent")\n        .output()\n        .expect("فشل تنفيذ الأمر");\n\n    if output.status.success() {\n        let stdout = String::from_utf8_lossy(&output.stdout);\n        println!("نجاح: {}", stdout);\n    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        eprintln!("خطأ (الكود {}): {}", output.status.code().unwrap_or(-1), stderr);\n    }\n}\n
\n\n

هنا نستخدم String::from_utf8_lossy() — طريقة آمنة لتحويل البايتات إلى سلسلة نصية، تستبدل الأحرف غير الصالحة لـ UTF-8 بـ �. هذا أمر بالغ الأهمية إذا كان مخرجات الأمر تحتوي على بيانات ثنائية أو تستخدم ترميزًا غير متوقع.

\n\n

تقنيات متقدمة: البرمجة غير المتزامنة والأنابيب

\n\n

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

\n\n

التقاط غير متزامن للمخرجات مع Tokio

\n\n

أضف التبعية إلى Cargo.toml:

\n\n
[dependencies]\ntokio = { version = "1", features = ["full"] }\n
\n\n

الآن لننفذ الأمر بشكل غير متزامن:

\n\n
use tokio::process::Command;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let output = Command::new("sleep")\n        .arg("2")\n        .output()\n                

المدونات

توصيات الكتب