Récupérer les arguments de la ligne de commande
Créons un nouveau projet comme à l'accoutumée avec cargo new
. Appelons
notre projet minigrep
pour le distinguer de l'outil grep
que vous avez
probablement déjà sur votre système.
$ cargo new minigrep
Created binary (application) `minigrep` project
$ cd minigrep
La première tâche est de faire en sorte que minigrep
utilise ses deux
arguments en ligne de commande : le nom du fichier et la chaîne de caractères à
rechercher. Autrement dit, nous voulons pouvoir exécuter notre programme avec
cargo run
, une chaîne de caractères à rechercher, et un chemin vers un
fichier dans lequel chercher, comme ceci :
$ cargo run chaine_a_chercher fichier-exemple.txt
Pour l'instant, le programme généré par cargo new
ne peut pas traiter les
arguments que nous lui donnons. Certaines bibliothèques qui existent sur
crates.io peuvent vous aider à écrire un programme
qui prend des arguments en ligne de commande, mais comme vous apprenez
juste ce concept, implémentons cette capacité par nous-mêmes.
Lire les valeurs des arguments
Pour permettre à minigrep
de lire les valeurs des arguments de la ligne de
commande que nous lui envoyons, nous allons avoir besoin d'une fonction fournie
par la bibliothèque standard de Rust, qui est std::env::args
. Cette fonction
retourne un itérateur des arguments de la ligne de commande qui ont été donnés
à minigrep
. Nous verrons les itérateurs plus précisément au
chapitre 13. Pour l'instant, vous avez juste à savoir
deux choses à propos des itérateurs : les itérateurs engendrent une série de
valeurs, et nous pouvons appeler la méthode collect
sur un itérateur pour le
transformer en collection, comme les vecteurs, qui contiennent tous les
éléments qu'un itérateur engendrent.
Utilisez le code de l'encart 12-1 pour permettre à votre programme minigrep
de lire tous les arguments qui lui sont envoyés et ensuite collecter les
valeurs dans un vecteur.
Fichier : src/main.rs
use std::env; fn main() { let args: Vec<String> = env::args().collect(); println!("{:?}", args); }
D'abord, nous importons le module std::env
dans la portée avec une
instruction use
afin que nous puissions utiliser sa fonction args
. Notez
que la fonction std::env::args
est imbriquée sur deux niveaux de modules.
Comme nous l'avons vu dans le chapitre 7,
il est courant d'importer le module parent dans la portée plutôt que la
fonction. En faisant ainsi, nous pouvons facilement utiliser les autres
fonctions de std::env
. C'est aussi moins ambigü que d'importer uniquement
std::env::args
et ensuite d'appeler la fonction avec seulement args
, car
args
peu facilement être confondu avec une fonction qui est définie dans le
module courant.
La fonction
args
et l'unicode invalideNotez que
std::env::args
va paniquer si un des arguments contient de l'unicode invalide. Si votre programme a besoin d'utiliser des arguments qui contiennent de l'unicode invalide, utilisez plutôtstd::env::args_os
à la place. Cette fonction retourne un itérateur qui engendre des valeursOsString
plutôt que des valeursString
. Nous avons choisi d'utiliser icistd::env::args
par simplicité, car les valeursOsString
diffèrent selon la plateforme et c'est plus complexe de travailler avec par rapport aux valeurs de typeString
.
Dans la première ligne du main
, nous appelons env::args
, et nous utilisons
immédiatement collect
pour retourner un itérateur dans un vecteur qui
contient toutes les valeurs engendrées par l'itérateur. Nous pouvons utiliser
la fonction collect
pour créer n'importe quel genre de collection, donc nous
avons annoté explicitement le type de args
pour préciser que nous attendions
un vecteur de chaînes de caractères. Bien que nous n'ayons que très
rarement d'annoter les types en Rust, collect
est une fonction que vous
aurez souvent besoin d'annoter car Rust n'est pas capable de déduire le type
de collection que vous attendez.
Enfin, nous affichons le vecteur en utilisant la chaîne de formatage :?
.
Essayons d'abord de lancer le code sans arguments, puis ensuite avec deux
arguments :
$ cargo run
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/minigrep`
["target/debug/minigrep"]
$ cargo run aiguille botte_de_foin
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 1.57s
Running `target/debug/minigrep aiguille botte_de_foin`
["target/debug/minigrep", "aiguille", "botte_de_foin"]
Remarquez que la première valeur dans le vecteur est
"target/debug/minigrep"
, qui est le nom de notre binaire. Cela correspond
au fonctionnement de la liste d'arguments en C, qui laissent les programmes
utiliser le nom sous lequel ils ont été invoqués dans leur exécution. C'est
parfois pratique pour avoir accès au nom du programme dans le cas où vous
souhaitez l'afficher dans des messages, ou changer le comportement du programme
en fonction de ce que l'alias de la ligne de commande utilise pour invoquer le
programme. Mais pour les besoins de ce chapitre, nous allons l'ignorer et
récupérer uniquement les deux arguments dont nous avons besoin.
Enregistrer les valeurs des arguments dans des variables
L'affichage des valeurs du vecteur des arguments nous a démontré que le programme peut avoir accès aux valeurs envoyées en arguments d'une ligne de commande. Maintenant, nous avons besoin d'enregistrer les valeurs des deux arguments dans des variables afin que nous puissions utiliser les valeurs pour le reste du programme. C'est que nous faisons dans l'encart 12-2.
Fichier : src/main.rs
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let recherche = &args[1];
let nom_fichier = &args[2];
println!("On recherche : {}", recherche);
println!("Dans le fichier : {}", nom_fichier);
}
Comme nous l'avons vu lorsque nous avons affiché le vecteur, le nom du
programme prend la première valeur dans le vecteur, dans args[0]
, donc nous
allons commencer à l'indice 1
. Le premier argument que prend minigrep
est
la chaîne de caractères que nous recherchons, donc nous insérons la référence
vers le premier argument dans la variable recherche
. Le second argument sera
le nom du fichier, donc nous insérons une référence vers le second argument
dans la variable nom_fichier
.
Nous affichons temporairement les valeurs de ces variables pour prouver que le
code fonctionne bien comme nous le souhaitons. Lançons à nouveau ce programme
avec les arguments test
et example.txt
:
$ cargo run test exemple.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/minigrep test exemple.txt`
On recherche : test
Dans le fichier : exemple.txt
Très bien, notre programme fonctionne ! Les valeurs des arguments dont nous avons besoin sont enregistrées dans les bonnes variables. Plus tard, nous allons ajouter de la gestion d'erreurs pour pallier aux potentielles situations d'erreurs, comme lorsque l'utilisateur ne fournit pas d'arguments ; pour le moment, nous allons ignorer ces situations et continuer à travailler pour l'ajout d'une capacité de lecture de fichier, à la place.