cli icon indicating copy to clipboard operation
cli copied to clipboard

How to put generic arguments before flags in v2

Open yirez-tc opened this issue 3 years ago • 4 comments

I've been trying to place generic arguments first when running a cli command, like this: testrun aaa -lang spanish

Following along the spanish example in the tutorial here

What I get back is everything has become an argument. If I do it the other way round, with the argument at the back everything works fine. Is there a flag I'm missing to set for it to work in the initial way. I put up a stackoverflow question up too but didn't get any responses to that.

urfave/cli version: v2.14.1 golang v: 1.18 stackoverflow question link: https://stackoverflow.com/questions/73587275/urfave-cli-argument-placement-prevents-flag-parsing

yirez-tc avatar Sep 06 '22 11:09 yirez-tc

Hi @yirez-tc

Why would you put flags/options at the end ? The usual way of running a cli would be the other way around, flags/options first and then arguments.

I can think about few examples from the linux world:

mkdir.exe r --help
Usage: mkdir [OPTION]... DIRECTORY...
...

or

rmdir --help
Usage: rmdir [OPTION]... DIRECTORY...

or

ls --help
Usage: ls [OPTION]... [FILE]...

I'm not saying that it should not be possible, but the standard way is options first, then args.

abousselmi avatar Sep 06 '22 20:09 abousselmi

Hey @abousselmi ,

We can make do with putting it at the end but there was a specific requirement for a cli tool we were building. It had several flags and one of the flags was to be the first argument implicitly since it will be used the most often. The intention was to be a bit like an sql command.

something like below where this,that argument could be used without explicitly typing -oftenUsedFlag instead. find this,that -oftenUsedFlag value -otherflag value ...

yirez-tc avatar Sep 07 '22 12:09 yirez-tc

Hey @yirez-tc ,

The CLI help is pretty clear about the order of flags and args. You cannot have args before global options or command options. A dummy find cli would print the following:

NAME:
   find - Usage of my cli

USAGE:
   find [global options] command [command options] [arguments...]

To achieve what you are asking, you can do something like :

func main() {
	app := &cli.App{
		Name:  "find",
		Usage: "Usage of my cli",

		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:  "oftenUsedFlag",
				Value: "val",
				Usage: "My flag 1",
			},
			&cli.StringFlag{
				Name:  "otherflag",
				Value: "other val",
				Usage: "My flag 2",
			},
			&cli.StringFlag{
				Name:  "arg",
				Value: "false arg",
				Usage: "My false arg",
			},
		},

		Action: func(cCtx *cli.Context) error {
			fmt.Println(cCtx.String("oftenUsedFlag"))
			fmt.Println(cCtx.String("otherflag"))

			fmt.Println("Hello", cCtx.String("arg"))
			return nil
		},
	}
        if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

Then you can create an alias and execute it like this:

$ alias find="./find -arg"

which gives you the requested behavior, using only flags, with no args around

$ find this,that -oftenUsedFlag f1 -otherflag f2
f1
f2
Hello this,that

abousselmi avatar Sep 07 '22 21:09 abousselmi

Why would you put flags/options at the end ? The usual way of running a cli

Both

git push origin main --force

and

git push --force origin main

work equally well.

I guess the limitation in this package is due to the behavior of flagSet.Parse() (see also https://github.com/golang/go/issues/4513, https://github.com/golang/go/issues/24107, https://github.com/golang/go/issues/36744).

tschaub avatar Sep 12 '22 14:09 tschaub

use gopkg.in/alecthomas/kingpin.v2

zhujintao avatar Oct 09 '22 13:10 zhujintao

Duplicate of #1113

dearchap avatar Oct 21 '22 19:10 dearchap