من Python إلى Rust: تطور تنفيذ الأوامر
\n\nإذا كنت قد تساءلت يومًا "How do I invoke a system command and capture its output?" على Stack Overflow، فأنت تعلم: هذا أحد أكثر الأسئلة شيوعًا وحيوية لأي مطور. في عالم Python، الإجابة بسيطة — subprocess.check_output(). ولكن ماذا تفعل إذا انتقلت إلى Rust وتريد ليس فقط تشغيل أمر، بل القيام بذلك بأناقة وأمان وبأقصى أداء؟
Rust ليست مجرد "لغة أخرى". إنها نظام حيث كل بايت تحت السيطرة، وأخطاء تنفيذ الأوامر لا تؤدي إلى انهيار التطبيق بأكمله. في عام 2025، عندما أصبح الأمان والسرعة معيارًا واقعيًا، فإن القدرة على العمل بشكل صحيح مع العمليات الفرعية في Rust هي مهارة لا غنى عنها. دعنا نستعرض أفضل الممارسات التي تحول تشغيل أوامر النظام من روتين إلى فن.
\n\nstd::process::Command: سكينك السويسري
\n\nقلب تنفيذ الأوامر في Rust هو الوحدة std::process::Command. توفر كل ما هو ضروري لتشغيل البرامج الخارجية، والتقاط مخرجاتها، ومعالجة الأخطاء. على عكس Python، حيث يخفي subprocess العديد من التفاصيل، يمنحك Rust السيطرة الكاملة.
مثال أساسي: التقاط stdout
\n\nلنبدأ بالكلاسيكيات — تنفيذ الأمر echo "Hello, Rust!" والتقاط مخرجاته:
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 على معالجة الأعطال المحتملة بشكل صريح: قد لا يكون الأمر موجودًا، أو لا يملك صلاحيات الوصول، أو ببساطة ينتهي بخطأ.
معالجة stderr ورمز الإرجاع
\n\nفي السيناريوهات الواقعية، لا تحتاج فقط إلى stdout بل أيضًا إلى stderr. تخيل أنك تشغل ls /nonexistent — أمر سيؤدي بالتأكيد إلى خطأ:
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في عام 2025، التنفيذ المتزامن للأوامر هو أمر غير عصري. التطبيقات الحديثة تتطلب البرمجة غير المتزامنة، خاصة إذا كنت تعمل مع خوادم الويب أو تعالج أوامر متعددة في وقت واحد. توفر Rust مع نظامها البيئي Tokio حلاً أنيقًا.
\n\nالتقاط غير متزامن للمخرجات مع Tokio
\n\nأضف التبعية إلى Cargo.toml:
[dependencies]\ntokio = { version = "1", features = ["full"] }\n\n\nالآن لننفذ الأمر بشكل غير متزامن:
\n\nuse 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