How to pass flags as arguments in cobra (golang)? - go

I am using cobra to create a CLI application (app). I need to implement a behavior in which I can pass flags as arguments. These flags will be passed|used further to another application via exec.Command(). All flag arguments passed to app must be ignored by the app itself, i.e. not recognized as flags.
I do not rule out that this is strange behavior. But if there is an opportunity to implement I will be grateful.
Examples of interaction with the application:
> app --help
or
> app --first --second
I want the arguments (--help --first --second, and all others) to not be taken as flags for app.

You can pass them after a double dash -- which by convention indicates that following flags and args are not meant to be interpreted as args and flags for the main program. They are ignored unless you explicitly use them in some way. i.e.:
if len(pflag.Args()) != 0 {
afterDash := pflag.Args()[pflag.CommandLine.ArgsLenAtDash():]
fmt.Printf("args after dash: %v\n", afterDash)
}
$ go run ./cmd/tpl/ -d yaml -- --foo --bar
args after dash: [--foo --bar]
cobra is using the pflag package https://pkg.go.dev/github.com/spf13/pflag

Related

Cannot insert dash in cobra parameters

I looked for some similar problems but I couldn't find anything except this: https://github.com/spf13/cobra/issues/1025
My problem is about inserting some string which contains a dash at the beginning like the following example,
go run myapp exampleCmd set "-Dexample"
Cobra seems to take the input -Dexample as internal parameter because returns this output:
Error: unknown shorthand flag: 'D' in -Dexample
Usage:
myapp exampleCmd set [flags]
Flags:
-h, --help help for set
Global Flags:
-s, --set string Set exampleCmd parameters. (default "default_param")
my init() function contains these two lines:
func init() {
...
exampleCmd.PersistentFlags().StringP("set", "s", defaultArgument, "Set parameters.")
exampleCmd.AddCommand(setCmd)
...
}
var exampleCmd = &cobra.Command{
Use: "set",
Short: "set parameter",
Long: `set parameter`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 && len(args) != 0 {
color.Red("Wrong usage, insert just a parameter")
} else if len(args) == 0 {
color.Yellow("Setting default parameter: " + defaultArgument)
internal.SetParams(defaultArgument)
} else {
internal.SetParams(args[0])
}
return nil
},
}
How can I accept parameters with dashes at beginning with cobra, if exists any solution?
As with virtual all Unix-style command line utilities and flag parsing libraries, Cobra separates flags from arguments with a --, after which no more arguments will be parsed as flags, even if they start with a -.
go run myapp exampleCmd set -- "-Dexample"
This is no different than how you interact with other CLI utilities. For example, rm -i sets the interactive flag, while rm -- -i removes a file named -i.
You definitely do not want to arbitrarily disable flags for certain commands or subcommands which is inconsistent (both within your own app and across all other apps), unnecessary, and breaks basic user expectations: Sometimes, -h will do what the user expects, but for some commands, for reasons the user cannot predict, -h will be treated as an argument and produce unexpected behavior.
Unix has solved this problem for more than 50 years. Let the user decide whether a argument is a flag via --.
Solved using this workaround (if it is)
I added to the end of the &cobra.Command{} this element:
DisableFlagParsing: true,
found here: https://github.com/spf13/cobra/issues/683
I don't think it is possible to pass an argument beginning with a dash symbol while using an utility like cobra. The dash is a flag indicator and it doesn't matter if it is enclosed in quotes, single dash is read as a shorthand flag, so the first letter of your input is getting interpreted as a flag and unrecognized, thus the program fails (and calls cmd.Help()).
You've set set as both command and a flag (--set -s), so it appears in your --help output.
I would consider using a different character for your command argument or adding it in another way internally.
I found the solution in the flagset interspersed option:
https://github.com/spf13/cobra/issues/1307#issue-777308697
This says to cobra that other flags after the first are also flags.

Perl6 REPL usage

Is it possible to have (Rakudo) Perl6 execute some code before dropping you into the REPL? Like python does with "python -i ".
For instance, I want to load up some modules and maybe read a side file and build some data structures from that side file before dropping into the REPL and letting the user do the things they need to do on the data structure, using the REPL as a user interface.
This is similar but different than Start REPL with definitions loaded from file though answers to this question might satisfy that one. The basic case is that, at the end of execution of any program, instead of exiting, the interpreter leaves the user at the REPL. Aside from providing a nifty, built-in, Perl6-based user interface for interactive programs, it also provides a good tool from which to debug code that otherwise exits with an error.
edit:
Selecting Zoffix's solution as the correct (so far) one as it is the only one that satisfies all requirements as stated. Here's hoping this capability gets added to the compiler or language spec.
You can load modules with the -M switch.
$ perl6 -MJSON::Tiny
To exit type 'exit' or '^D'
> to-json Array.new: 1,2,3.Str
[ 1, 2, "3" ]
>
If you want to run other code, currently you have to put it into a module first.
$ mkdir lib
$ echo 'our $bar = 42' > lib/foo.pm6
$ perl6 -Ilib -Mfoo
To exit type 'exit' or '^D'
> $bar
42
>
I'd like to provide an answer that Zoffix gave on IRC. It satisfies the basic requirement but is far from pretty and it uses NQP for which there is no user support nor is the NQP API ( "nqp::*" calls ) guaranteed for the future and can change without warning.
replify 「
say 'Hello to your custom REPL! Type `say $a` to print the secret variable';
my $a = "The value is {rand}";
」;
sub replify (Str:D \pre-code = '') {
use nqp;
my %adverbs; # command line args like --MFoo
my \r := REPL.new: nqp::getcomp('perl6'), %adverbs;
my \enc := %adverbs<encoding>:v.Str;
enc && enc ne 'fixed_8' && $*IN.set-encoding: enc;
my $*CTXSAVE := r;
my $*MAIN_CTX;
pre-code and r.repl-eval: pre-code, $, :outer_ctx(nqp::getattr(r, REPL, '$!save_ctx')),
|%adverbs;
$*MAIN_CTX and nqp::bindattr(r, REPL, '$!save_ctx', $*MAIN_CTX);
r.repl-loop: :interactive, |%adverbs;
}

How to document the rest of argv in golang flags?

As a microservices author, I appreciate how powerful the standard library's flag pacakge is, providing a lightweight way to document command line flags. However, the built-in -help option appears to present documentation for only the flags themselves, while the rest of the command line arguments are often idiosyncratic, requiring documentation as well. What is a good way to document the rest of the CLI arguments, such as a Go application that accepts some flags, and then treats the rest of the arguments as file paths?
My preferred approach is just to set flag.Usage to a function which prints the additional documentation.
For example:
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: %s [flags] <paths...>\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintf(os.Stderr, "Argument documention goes here\n")
}

What does -- do when running an npm command?

As an example, double dash or two hyphens -- is used like so:
npm test -- --coverage
Running npm without the double dash flag does not run in coverage mode so it seems to append subsequent flags, is this correct? I couldn't find the documentation on this.
-- as an argument on its own is standardized across all UNIX commands: It means that further arguments should be treated as positional arguments, not options. See Guideline 10 in POSIX Utility Syntax Conventions.
To give you a non-NPM-based example, ls -- -l will look for a file named -l, because the -- specified that all subsequent arguments are positional.
In this context, it means that --coverage isn't an option to npm itself; presumably, then, it's subsequently read by the test subcommand. For a tool that were following the conventions properly this wouldn't be necessary, because Guideline 9 specifies that all options shall be given before any arguments (thus that in this context --coverage should be treated as an argument since it comes after the argument test); however, inasmuch as NPM is only partially following the guidelines, this is a foreseeable result.
(Long --option-style options are actually a GNU extension as a whole, so what we have here is a mismash of multiple parsing styles; such is life, unfortunately).
I've done some further digging; according to the docs for my node version -
"--" Indicates the end of node options. Pass the rest of the arguments to the script. If no script filename or eval/print script is supplied prior to this, then the next argument will be used as a script filename.
But, a simple script that contains -
console.log(`process.execArgv:${process.execArgv}`);
console.log(`process.argv:${process.argv}`);
behaves as -
>node --prof argv.js --myArg
process.execArgv:--prof
process.argv:C:\Program Files\nodejs\node.exe,C:\Dev\Web\QA_Web_POC\argv.js,--myArg
>node --prof argv.js -- --myArg
process.execArgv:--prof
process.argv:C:\Program Files\nodejs\node.exe,C:\Dev\Web\QA_Web_POC\argv.js,--, --myArg
>node argv.js --prof -- --myArg
process.execArgv:
process.argv:C:\Program Files\nodejs\node.exe,C:\Dev\Web\QA_Web_POC\argv.js,--prof,--,--myArg
>node argv.js -- --prof --myArg
process.execArgv:
process.argv:C:\Program Files\nodejs\node.exe,C:\Dev\Web\QA_Web_POC\argv.js,--,--prof,--myArg
So, it seems there's a bug?

perl Getopt::Long::GetOptions usage

I wanted to use Getopt::Long::GetOptions for getting command line options to the script.
I have a requirement like this:
perl script.pl -c <name1> -c <name2> -m <name3> argument
Here we have option flags -c and -mm which are optional, and argument is mandatory.
Can anyone point out the correct usage for GetOptions?
From the Getopt::Long documentation:
GetOptions does not return a false result when an option is not supplied
That's why they're called 'options'.
In other words, if you are expecting a mandatory parameter, you need to explicitly check for it outside of the GetOptions call.
If argument is meant to be part of #ARGV and not the options, use -- to signal the end of options. In the example below, the script would access argument via $ARGV[0]:
perl script.pl -c <name1> -c <name2> -m <name3> -- argument
Here's a sample code and result.
https://gist.github.com/kyanny/5634832
If you want to know more about how to handle multiple values option, see documantation: http://perldoc.perl.org/Getopt/Long.html#Options-with-multiple-values
One more thing, Getopt::Long::GetOptions does not provide the way to handle mandatory options. You should check if the mandatory options are in the #ARGV and raise Exceptions, etc. in your hand.

Resources