os.Args
os.Args est une slice de chaînes. os.Args[0] est le nom du programme, le reste ce sont les arguments exactement tels que le shell les a transmis.
Dans le panneau Args de l'éditeur, chaque champ est un argument, transmis tel quel. Essayez hello, two words et -v dans trois champs : le programme voit trois arguments, et two words reste un seul argument avec un espace à l'intérieur. Dans un terminal, c'est le shell qui découpe, donc l'équivalent est go run . hello "two words" -v.
Vérifiez toujours len(os.Args) avant d'indexer. os.Args[1] sans argument provoque un panic index out of range [1] with length 1.
Les arguments sont des chaînes. Convertissez les nombres avec strconv.Atoi ou strconv.ParseFloat et gérez l'erreur, car les utilisateurs tapent n'importe quoi.
Le package flag
Pour des options comme -port 8080 -verbose, utilisez flag. Il analyse, convertit les types, signale les erreurs et génère un message d'aide.
Sans argument, ce programme affiche hello, world une fois. Dans le panneau Args, essayez -name et Gopher dans deux champs, puis -count=3, -loud et extra dans trois autres. Le programme affiche HELLO, GOPHER! trois fois et remaining args: [extra].
Comment fonctionnent les flags :
- Chaque fonction de définition (
flag.String,flag.Int,flag.Bool,flag.Float64,flag.Duration,flag.Uint64...) prend un nom, une valeur par défaut et un texte d'aide, et renvoie un pointeur. Lisez la valeur avec*nameaprèsflag.Parse(). - Les formes
Varse lient à une variable que vous avez déjà :flag.IntVar(&cfg.Port, "port", 8080, "port"). C'est plus propre pour une struct de configuration. - Les utilisateurs peuvent écrire
-name value,-name=value,--name valueou--name=value. Go ne fait aucune différence entre un et deux tirets. - Les booléens ont besoin de
=pour prendre une valeur.-loudmet true,-loud=falsemet false, mais-loud falsemet true et laissefalsecomme argument positionnel. - L'analyse s'arrête au premier argument qui n'est pas un flag (ou à
--).prog file.txt -vtraite-vcomme un argument positionnel. Mettez les flags en premier. flag.Args()renvoie les arguments positionnels restants,flag.NArg()leur nombre, etflag.Arg(i)l'un d'entre eux.
Un flag inconnu ou une valeur invalide affiche une erreur suivie de l'aide, puis sort avec le statut 2. -h ou -help affiche l'aide et sort avec le statut 0 (depuis Go 1.15). Le texte d'aide est généré à partir de vos définitions :
Usage of greet:
-count int
how many times (default 1)
-delay duration
pause between greetings, e.g. 10ms
-loud
shout the greeting
-name string
who to greet (default "world")
Affectez une fonction à flag.Usage pour afficher votre propre en-tête avant d'appeler flag.PrintDefaults().
Sous-commandes avec FlagSet
Des outils comme git commit -m msg ont des sous-commandes avec leurs propres flags. Créez un flag.FlagSet par sous-commande et choisissez-en un avec un switch sur le premier argument :
flag.ContinueOnError fait renvoyer une erreur à Parse au lieu de quitter, ce qui garde la fonction testable. run reçoit les arguments en paramètre au lieu de lire os.Args, donc un test peut appeler run([]string{"list", "-all"}) directement. Essayez list et -all dans le panneau Args, ou delete pour voir le chemin d'erreur.
Pour les grosses CLI avec des commandes imbriquées, l'autocomplétion shell et une documentation générée, la plupart des projets utilisent la bibliothèque tierce github.com/spf13/cobra. Le package standard flag suffit bien pour les petits outils.
Variables d'environnement
os.Getenvrenvoie""aussi bien quand la variable est absente que quand elle vaut une chaîne vide.os.LookupEnvfait la différence.- Les valeurs sont toujours des chaînes. Convertissez-les et validez-les au démarrage, et échouez avec un message clair plutôt qu'au milieu d'une requête.
os.Setenvaffecte le processus courant et les processus enfants lancés ensuite. Il ne peut pas modifier l'environnement du shell qui vous a lancé.os.Environ()renvoie toutes les variables sous forme de chaînes"KEY=value".
Une organisation courante de la configuration : des flags pour ce qu'une personne tape, des variables d'environnement pour les réglages de déploiement (les plateformes de conteneurs les définissent), les flags l'emportant sur l'environnement et l'environnement sur les valeurs par défaut.
Codes de sortie et os.Exit
Un programme Go sort avec le statut 0 quand main retourne. os.Exit(code) termine le processus immédiatement avec ce statut. Par convention, 0 signifie succès, 1 une erreur générale et 2 une erreur d'utilisation (le package flag utilise 2).
os.Exit n'exécute pas les fonctions différées. Les fichiers ne sont pas vidés et le nettoyage par defer est sauté. log.Fatal appelle os.Exit(1) et a le même effet. Gardez os.Exit à un seul endroit, à la fin de main, et mettez le vrai programme dans une fonction run qui renvoie une erreur, comme dans l'exemple des sous-commandes :
func main() {
if err := run(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
}
Écrivez les messages d'erreur sur os.Stderr, pas sur os.Stdout, pour qu'ils restent visibles quand la sortie est redirigée vers un fichier ou envoyée à une autre commande. Un panic non récupéré sort avec le statut 2.
Erreurs courantes
- Indexer
os.Argssans vérifier sa longueur. Les arguments manquants provoquent un panic. - Lire les flags avant
flag.Parse(). Vous obtenez les valeurs par défaut. - Oublier le
*.fmt.Println(port)affiche une adresse comme0xc000012345, pas la valeur. - Mettre les flags après les arguments positionnels.
prog input.txt -vn'analyse pas-v. -verbose falsepour un flag booléen. Écrivez-verbose=false.- Appeler
os.Exitoulog.Fatalau fond du programme. Le nettoyage différé ne s'exécute jamais et le code ne peut pas être testé. Remontez les erreurs jusqu'àmain.
Questions fréquentes
Comment récupérer les arguments de ligne de commande en Go ?
os.Args est un []string qui contient le nom du programme à l'indice 0 et les arguments ensuite. os.Args[1:] sont les arguments tapés par l'utilisateur. Vérifiez len(os.Args) avant d'indexer, sinon le programme provoque un panic quand un argument manque.
Comment utiliser le package flag en Go ?
Déclarez les flags, appelez flag.Parse(), puis lisez-les : port := flag.Int("port", 8080, "port to listen on"), flag.Parse(), fmt.Println(*port). Les fonctions renvoient des pointeurs. Les utilisateurs écrivent -port=9000, -port 9000 ou --port 9000, et -h affiche l'aide générée.
Comment lire une variable d'environnement en Go ?
os.Getenv("HOME") renvoie la valeur, ou une chaîne vide si la variable n'est pas définie. Pour distinguer une variable absente d'une variable vide, utilisez v, ok := os.LookupEnv("HOME"). os.Setenv modifie l'environnement du processus courant et des processus enfants qu'il lance ensuite.
os.Exit exécute-t-il les fonctions différées en Go ?
Non. os.Exit termine le processus immédiatement avec le code de statut donné, et les appels différés ne s'exécutent pas, donc une sortie en buffer peut être perdue et des fichiers peuvent ne pas être vidés. Un motif courant est func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) } }, avec tout le vrai travail et les defers dans run.