I'm creating a CLI tool and in that i want to accept value of string for "-l" flag. if the "-l" flag is passed without argument then use the empty string value.
when i run this with empt argument:app.exe -l
I'm running into error: flag needs an argument: -l
Code block: flag.StringVar(&listening, "l", "", "Receive only")
how can i solve this error?
bool type work with empty arguments but this case also accept string like: app.exe -l "127.0.0.1"
I'm using "flag" Go package.
Context: I'm creating a tool to send data using the ICMP protocol. In that, the "-l" flag can be used in two ways: if it is present, it acts as a bool value, and it starts listening for all incoming ICMP packets. If we want to filter those packets to receive from a specific IP only, then it will accept a string argument like "-l 192.168.56.27". In that case, it will listen for incoming packets, but it will discard all those with non-matching IPs and only process the ones that match the IP 192.168.56.27.
Note: This problem is now solved. I may have done it in a complicated way, but it is working😊. If you are curious, you can check out the code at GitHub: https://github.com/Aftab700/pingo
I will try my best to explain: I'm defining a custom flag type that implements the flag.Value
interface.
type stringFlag struct {
  set  bool
  value string
}
But this will still give an error if "-l" is empty. To deal with that, we will use flag.ContinueOnError
in flag.NewFlagSet
. and we will check if
err.Error() == "flag needs an argument: -l"
then we set the our stringFlag.set = true
.
But this will introduce a new issue: if we pass arguments like -l -h
then the -h
will not be considered a separate flag but a string value to the -l
.
To deal with that, I need to modify the os.Args
array before passing it to myFlagSet.Parse(Args)
.
We will check if the -l
is present, and if yes then if the next item in the array starts with -
character, we will append an empty string, so the -h
flag is considered a separate flag.
Code:
package main
import (
"flag"
"fmt"
"os"
)
var listeningStr stringFlag
var myFlagSet = flag.NewFlagSet("Options", flag.ContinueOnError)
type stringFlag struct {
set bool
value string
}
func (sf *stringFlag) Set(x string) error {
sf.value = x
sf.set = true
return nil
}
func (sf *stringFlag) String() string {
return sf.value
}
func parseFlags() {
myFlagSet.Var(&listeningStr, "l", "Listen for incoming ICMP packets\nProvide an IP address to Receive ICMP packets from provided IP address only")
// -l is string so if there is flag after -l[-l -h] will not consider -h as flag but value to -l
var Args []string
var temp = len(os.Args)
for i, v := range os.Args[1:] {
Args = append(Args, v)
if v == "-l" && (i+2) < temp && os.Args[i+2][0] == 45 { // 45 = "-"
Args = append(Args, "")
}
}
err := myFlagSet.Parse(Args)
myFlagSet.SetOutput(nil)
if err != nil {
if err.Error() == "flag needs an argument: -l" {
listeningStr.set = true
} else {
fmt.Println(err)
os.Exit(0)
}
}
}
func init() {
parseFlags()
}
For more info checkout the code at GitHub: https://github.com/Aftab700/pingo/blob/main/main.go