Exécution de commandes en Rust : comment capturer la sortie en 2025

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

De Python à Rust : l'évolution de l'exécution des commandes

\n\n

Si vous vous êtes déjà demandé « How do I invoke a system command and capture its output? » sur Stack Overflow, vous savez que c'est l'une des questions les plus populaires et essentielles pour tout développeur. Dans le monde Python, la réponse est simple — subprocess.check_output(). Mais que faire si vous êtes passé à Rust et que vous voulez non seulement exécuter une commande, mais le faire avec élégance, sécurité et performances maximales ?

\n\n

Rust n'est pas simplement « un autre langage ». C'est un système où chaque octet est sous contrôle, et où les erreurs d'exécution des commandes n'entraînent pas le crash de toute l'application. En 2025, alors que la sécurité et la vitesse sont devenues le standard de facto, savoir travailler correctement avec les sous-processus en Rust est une compétence indispensable. Analysons les meilleures pratiques qui transformeront l'exécution de commandes système d'une routine en un art.

\n\n

std::process::Command : votre couteau suisse

\n\n

Le cœur de l'exécution des commandes en Rust est le module std::process::Command. Il fournit tout le nécessaire pour lancer des programmes externes, capturer leur sortie et gérer les erreurs. Contrairement à Python, où subprocess masque de nombreux détails, Rust vous donne un contrôle total.

\n\n

Exemple de base : capture de stdout

\n\n

Commençons par un classique — exécutons la commande echo "Hello, Rust!" et capturons sa sortie :

\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!("Sortie de la commande : {}", stdout);\n\n    Ok(())\n}\n
\n\n

Qu'est-ce qui est important ici ? Nous utilisons .output(), qui retourne une structure Output. Elle contient trois champs : stdout, stderr et status. Notez l'utilisation de ? — c'est l'opérateur de propagation d'erreur qui nous évite des blocs match volumineux. Rust nous oblige à gérer explicitement les échecs possibles : la commande peut ne pas exister, ne pas avoir les droits d'accès ou simplement se terminer avec une erreur.

\n\n

Gestion de stderr et du code de retour

\n\n

Dans les scénarios réels, vous avez besoin non seulement de stdout, mais aussi de stderr. Imaginez que vous exécutez ls /nonexistent — une commande qui générera à coup sûr une erreur :

\n\n
use std::process::Command;\n\nfn main() {\n    let output = Command::new("ls")\n        .arg("/nonexistent")\n        .output()\n        .expect("Impossible d'exécuter la commande");\n\n    if output.status.success() {\n        let stdout = String::from_utf8_lossy(&output.stdout);\n        println!("Succès : {}", stdout);\n    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        eprintln!("Erreur (code {}) : {}", output.status.code().unwrap_or(-1), stderr);\n    }\n}\n
\n\n

Ici, nous utilisons String::from_utf8_lossy() — une méthode sécurisée pour convertir des octets en chaîne, qui remplace les caractères UTF-8 invalides par �. C'est crucial si la sortie de la commande contient des données binaires ou utilise un encodage imprévisible.

\n\n

Techniques avancées : asynchronisme et pipes

\n\n

En 2025, l'exécution synchrone des commandes est démodée. Les applications modernes nécessitent de l'asynchronisme, surtout si vous travaillez avec des serveurs web ou traitez plusieurs commandes simultanément. Rust, avec son écosystème Tokio, offre une solution élégante.

\n\n

Capture asynchrone de la sortie avec Tokio

\n\n

Ajoutons la dépendance dans Cargo.toml :

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

Exécutons maintenant la commande de manière asynchrone :

\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                

Blogs

Book Recommendations