web-dev-qa-db-fra.com

Les indicateurs de ligne de commande dans Go peuvent-ils être définis comme obligatoires?

Existe-t-il un moyen de définir que certains indicateurs sont obligatoires, ou dois-je vérifier leur présence par moi-même?

34
Petr Pudlák

Le package flag ne prend pas en charge les indicateurs obligatoires ou obligatoires (ce qui signifie que l'indicateur doit être spécifié explicitement).

Ce que vous pouvez faire, c'est utiliser des valeurs par défaut raisonnables pour (tous) les indicateurs. Et si un indicateur est quelque chose comme s'il n'y a pas de défaut sensible, vérifiez la valeur au début de votre application et arrêtez-vous avec un message d'erreur. Vous devriez quand même faire la validation de la valeur des indicateurs (pas seulement pour les indicateurs requis), donc cela ne devrait pas signifier de (gros) frais généraux, et c'est une bonne pratique en général.

24
icza

Comme déjà mentionné , le package flag ne fournit pas cette fonctionnalité directement et généralement vous pouvez (et devriez) être en mesure de fournir une valeur par défaut raisonnable. Pour les cas où vous n'avez besoin que d'un petit nombre d'arguments explicites (par exemple, un nom de fichier d'entrée et de sortie), vous pouvez utiliser des arguments de position (par exemple après flag.Parse() vérifiez que flag.NArg()==2 puis input, output := flag.Arg(0), flag.Arg(1)).

Si toutefois, vous avez un cas où cela n'est pas raisonnable; dites quelques drapeaux entiers que vous souhaitez accepter dans n'importe quel ordre, où toute valeur entière est raisonnable, mais aucune valeur par défaut ne l'est. Ensuite, vous pouvez utiliser la fonction flag.Visit pour vérifier si les indicateurs qui vous intéressent ont été explicitement définis ou non. Je pense que c'est la seule façon de savoir si un indicateur a été explicitement défini sur sa valeur par défaut (sans compter un type flag.Value Personnalisé avec une implémentation Set qui a gardé son état).

Par exemple, peut-être quelque chose comme:

    required := []string{"b", "s"}
    flag.Parse()

    seen := make(map[string]bool)
    flag.Visit(func(f *flag.Flag) { seen[f.Name] = true })
    for _, req := range required {
        if !seen[req] {
            // or possibly use `log.Fatalf` instead of:
            fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
            os.Exit(2) // the same exit code flag.Parse uses
        }
    }

Playground

Cela produirait une erreur si l'indicateur "-b" ou "-s" n'était pas explicitement défini.

7
Dave C

go-flags vous permet de déclarer à la fois les drapeaux requis et les arguments positionnels requis:

var opts struct {
    Flag string `short:"f" required:"true" name:"a flag"`
    Args struct {
        First   string `positional-arg-name:"first arg"`
        Sencond string `positional-arg-name:"second arg"`
    } `positional-args:"true" required:"2"`
}
args, err := flags.Parse(&opts)
6
iKanor

J'aime github.com/jessevdk/go-flags package à utiliser en CLI. Il s'agit de l'attribut required, pour définir l'indicateur obligatoire. Comme ça:

var opts struct {
...
    // Example of a required flag
    Name string `short:"n" long:"name" description:"A name" required:"true"`
...
}
6
RoninDev

Si vous avez un chemin de drapeau, vérifiez simplement si * chemin contient une valeur

var path = flag.String("f", "", "/path/to/access.log")
flag.Parse()
if *path == "" {
    usage()
    os.Exit(1)
}
3
ivan73

Je suis d'accord avec cette solution mais, dans mon cas, les valeurs par défaut sont généralement des valeurs d'environnement. Par exemple,

dsn := flag.String("dsn", os.Getenv("MYSQL_DSN"), "data source name")

Et dans ce cas, je veux vérifier si les valeurs sont définies à partir de l'invocation (généralement le développement local) ou de l'environnement var (environnement prod).

Donc, avec quelques modifications mineures, cela a fonctionné pour mon cas.

Utiliser flag.VisitAll pour vérifier la valeur de tous les indicateurs.

required := []string{"b", "s"}
flag.Parse()

seen := make(map[string]bool)
flag.VisitAll(func(f *flag.Flag) {
    if f.Value.String() != "" {
        seen[f.Name] = true
    }
})
for _, req := range required {
    if !seen[req] {
        // or possibly use `log.Fatalf` instead of:
        fmt.Fprintf(os.Stderr, "missing required -%s argument/flag\n", req)
        os.Exit(2) // the same exit code flag.Parse uses
    }
}

Test example in plauground

0
canhizares