Control fmt.Parse output on Parsing Error - go

var timesFlag int
flag.IntVar(&timesFlag, "times", 1, "Number of times to print")
flag.Parse()
If I run the program and type in prog -times=abc (<--- not an int)
fmt.Parse spits out this ugly error message on the console:
invalid value "-abc" for flag -times: strconv.ParseInt: parsing "-abc": invalid syntax
Obviously, I can't prevent the user from typing in anything from the command line. The error looks like garbage to the user and needs to look more friendlier. How do I silent this error from going to stderr/stdout and detect there was an error generated?

The flag.CommandLine variable of type the FlagSet is used to handle command line arguments if you use the functions of the flag package which are not methods of the FlagSet type.
You can set the output (an io.Writer) where error messages are printed with the FlagSet.SetOutput() method. You can set a bytes.Buffer so messages will only end up in a buffer (and not on your console). Note that do not set nil as that means to print to the standard output (console).
And call FlagSet.Parse() yourself where you can pass os.Args[1:] as the arguments to be parsed. FlagSet.Parse() returns an error which you can handle yourself.

Related

input password to command in golang

I need to use lxd-p2c(https://github.com/lxc/lxd/tree/master/lxd-p2c) in golang code.
I try to pass password to the binary lxd-p2c built by the code above, which uses term.ReadPassword(0)(https://github.com/lxc/lxd/blob/master/lxd-p2c/utils.go#L166) to read password in Linux.
I did some search on the internet and tried following code but they just did not work.
# 1
cmd := exec.Command(command, args...)
cmd.Stdin = strings.NewReader(password)
# 2
cmd := exec.Command(command, args...)
stdin, _ := cmd.StdinPipe()
io.WriteString(stdin, password)
Similar but simple code to test: https://play.golang.org/p/l-9IP1mrhA (code from https://stackoverflow.com/a/32768479/9265302)
build the binary and call it in go.
edit:
No workaround found and I removed term.ReadPassword(0) in the source code.
Checking the error in your playground displays inappropriate ioctl for device.
Searching for the error message I found this thread, which notes that non-terminal input is not supported for terminal.ReadPassword. My guess is that passing Stdin input this way makes it pass the input with a character device instead of with the necessary terminal device like tty or any such, making the read fail. lxd-p2c can't read the password from such an input device.

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.

Checking whether a call to fmt.Errorf() failed [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
In Go, the function fmt.Errorf() returns an error interface.
What's the proper way to check if fmt.Errorf() itself failed?
fmt.Errorf returns basically a string that is wrapped as an error. If you provide wrong arguments like in the following example, you still get an error but with the error message of the format fault inside the string message:
package main
import "fmt"
func main() {
fmt.Println(fmt.Errorf("abc", 5))
}
outputs the error string:
abc%!(EXTRA int=5)
These functions are not expected to be used in a context where you do not know the arguments that you are providing. The typical use case is that you "test" your formatting code when you write it. In your output code you will spot these error messages easily. You should just make sure to test these calls before going into production.
The danger of a panic in these functions is negligible, panics are caught as stated in the documentation:
If an Error or String method triggers a panic when called by a print routine, the fmt
package reformats the error message from the panic, decorating it with an indication
that it came through the fmt package. For example, if a String method calls
panic("bad"), the resulting formatted message will look like
%!s(PANIC=bad)
You should try to catch errors at compile and test time, not run time. For fmt.Errorf, run go vet.
For example,
package main
import "fmt"
func main() {
err := fmt.Errorf("%v")
fmt.Println(err)
}
Output:
$ go vet errorf.go
# command-line-arguments
./errorf.go:6: Errorf format %v reads arg #1, but call has 0 args
$
$ go run errorf.go
%!v(MISSING)
$
Command vet
Vet examines Go source code and reports suspicious constructs, such as
Printf calls whose arguments do not align with the format string. Vet
uses heuristics that do not guarantee all reports are genuine
problems, but it can find errors not caught by the compilers.
Vet is normally invoked using the go command by running "go vet":
go vet
Printf family
Flag: -printf
Suspicious calls to functions in the Printf family, including any
functions with these names, disregarding case:
Print Printf Println
Fprint Fprintf Fprintln
Sprint Sprintf Sprintln
Error Errorf
Fatal Fatalf
Log Logf
Panic Panicf Panicln
The -printfuncs flag can be used to redefine this list. If the
function name ends with an 'f', the function is assumed to take a
format descriptor string in the manner of fmt.Printf. If not, vet
complains about arguments that look like format descriptor strings.
It also checks for errors such as using a Writer as the first argument
of Printf.
Formatted output errors, by design, are a special case. For example, an error when reporting an error can be circular.
Package fmt
Format errors:
If an invalid argument is given for a verb, such as providing a string
to %d, the generated string will contain a description of the problem,
as in these examples:
Wrong type or unknown verb: %!verb(type=value)
Printf("%d", hi): %!d(string=hi)
Too many arguments: %!(EXTRA type=value)
Printf("hi", "guys"): hi%!(EXTRA string=guys)
Too few arguments: %!verb(MISSING)
Printf("hi%d"): hi%!d(MISSING)
Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi
Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
Invalid or invalid use of argument index: %!(BADINDEX)
Printf("%*[2]d", 7): %!d(BADINDEX)
Printf("%.[2]d", 7): %!d(BADINDEX)
All errors begin with the string "%!" followed sometimes by a single
character (the verb) and end with a parenthesized description.
If an Error or String method triggers a panic when called by a print
routine, the fmt package reformats the error message from the panic,
decorating it with an indication that it came through the fmt package.
For example, if a String method calls panic("bad"), the resulting
formatted message will look like
%!s(PANIC=bad)
The %!s just shows the print verb in use when the failure occurred. If
the panic is caused by a nil receiver to an Error or String method,
however, the output is the undecorated string, "".
Errorf formats according to a format specifier and returns the string
as a value that satisfies error. The fmt package's Errorf function lets us use the package's formatting features to create descriptive error messages.
func Errorf(format string, a ...interface{}) error {
return errors.New(Sprintf(format, a...))
}
And New returns an error that formats as the given text.
func New(text string) error {
return &errorString{text}
}
And errorString is a trivial implementation of error.
type errorString struct {
s string
}
So Basically what it is returning is field of string type.

Using cgo, why does C output not 'survive' piping when golang's does?

I'm experimenting with cgo to use C code from golang, but in my little hello-world test, I've ran into something I can't understand or find more information about.
I'm starting with a simple test similar to examples I've found
package main
import (
"fmt"
"unsafe"
)
/*
#import <stdio.h>
#import <stdlib.h>
*/
import "C"
func main() {
go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
fmt.Printf("Printed from golang fmt\n")
}
This simple example just echoes strings to stdout from both golang (using fmt.Printf) and raw C (using C.puts) via the basic cgo binding.
When I run this directly in my terminal, I see both lines:
$ ./main
Printed from C.puts
Printed from golang fmt
When I run this but redirect output in any way – pipe to less, shell redirection to a file, etc – I only see golang's output:
./main | cat
Printed from golang fmt
What happens to the C.puts content when piping / redirecting?
Secondary questions: Is this a cgo quirk, or a c standard library quirk I'm not aware of? Is this behaviour documented? How would I go about debugging this on my own (e.g. is there a good/plausible way for me to 'inspect' what FD1 really is in each block?)
Update: If it's relevant, I'm using go version go1.6.2 darwin/amd64.
This is C behavior you're seeing.
Go does not buffer stdout, while in C it is usually buffered. When the C library detects stdout is a tty, it may use line buffering, so the additional \n inserted by puts will cause the output to be displayed.
You need to flush stdout to ensure you get all the output:
go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
C.fflush(C.stdout)
fmt.Printf("Printed from golang fmt\n")
See also
Why does printf not flush after the call unless a newline is in the format string?
Is stdout line buffered, unbuffered or indeterminate by default?
The C library buffering is per line, so the first line can be left in the buffer before it is properly flushed (done at exit time in C programs). You can either try to flush stdout, or try adding a trailing \n in the first string. Does it work if you add the \n?

%!B(MISSING) different output between fmt.Printf and log.Println

I have some bytes returned from json.Marshal. If log them to stdout like this:
log.Println(string(b))
They are output like this:
{"encoded":"%2B"}
If I write them to disk with
fmt.Fprintf(w, string(b))
And then cat the file they have been written like this:
{"encoded":"%!B(MISSING)"}
As far as I can tell the output of string(b) really is the first, and my expected, output. What am I doing wrong?
Fprintf takes a format definition as the first argument. The "%2B" is interpreted as a formatting directive and you are missing the following argument.
Maybe you wanted to use Fprint?

Resources