I'm currently writing my first small tool in Go and I ran into the problem that no matter what I do, one command line argument is getting ignored.
mainFile := flag.String("input", "./generator.ini", "the input file")
outputFile := flag.String("foo", "Default directory foo bar blablabla", "the output directory")
fmt.Println("Param: ", *outputFile)
outputDir := filepath.Clean(*outputFile)
flag.Parse()
fmt.Println("Outputdir ", outputDir)
fmt.Println("Mainfile ", *mainFile)
So the thing is that outputFile is always the default value no matter how I call the programm.
.\generator.exe -input=D:\entwicklung\GoLang\src\github.com\Abenstex\CodeGenerator\generator\main.cfg -out=D:\entwicklung\test
It absolutely does not matter if -out comes before -input or not. The value of output is always the default whereas the input value is always correctly set. I'm really at a loss here.
I found that I called flag.Parse() after I started using the parameter.
Related
I'm new to Golang, but one of its apparent strengths is in creating command-line tools so I thought a fun little learning exercise would be to recreate the 'touch' command on Windows. I wrote a program to create a new file at either a specified filepath, or in the current folder. This has gone absolutely grand, and I can make a new file no problem:
func create_empty_file(file_name string) bool {
filehandle, err := os.Create(file_name)
if err != nil {
return false
}
defer filehandle.Close()
return true
}
I know that this part is working because I can watch the file being made and open it after the program is complete. My problem here is that I would like to open this file in VSCode after it is created, and there is clearly something that I don't understand about os/exec. When I try to run this:
command_string := strings.Join([]string{"code", full_filepath}, " ")
run_command := exec.Command(command_string)
run_err := run_command.Run()
And I print out the contents of run_err (obviously checking for nil beforehand), I get this:
.Run() failed with error: exec: "code C:\\Go Code\\failed_successfully.go": file does not exist
If I copy and paste "code C:\\Go Code\\failed_successfully.go" into my command prompt, it opens the .go file in VSCode without issue, so clearly there is something about this call than I'm missing/don't understand.
I thought maybe it was trying to open the file before it had been created, so I looked up how to check if a file exists yet and then wrote a short function using Ticks which checks every few milliseconds if the file exists yet and returns true when it finds it. Otherwise, it runs for some specified number of seconds and then returns false. I still get the same error, so I am assuming that this is not the issue.
The last thing I did was to use strings.Replace() to replace all of the back-slashes with forward-slashes, which has no effect.
Any advice on how to achieve what I want here would be much appreciated!
exec.Command does not parse the input string, splitting it on spaces and so on. Instead, pass the arguments individually to exec.Command.
That is:
runCmd := exec.Command("code", full_filepath)
Currently, you're trying to find a command called code C:\\Go Code\\failed_successfully.go -- rather than one simply called code and calling it with an argument.
I am using Inno Download Plugin to download a bunch of files for my installation. These files are listed in "files.json".
FileList := TStringList.Create;
FileList.LoadFromFile(ExpandConstant('{tmp}\files.json'));
for i := 0 to FileList.Count-1 do
begin
fileName := ExtractFileName(FileList[i]);
StringChangeEx(FileList[i], '\', '/',True);
//Add each file to download queque
idpAddFile("www.myapiaddress.com/files/" + FileList[i], ExpandConstant('{tmp}\install\') + fileName );
Log(FileList[i]);
end;
What gives me a headache is the line StringChangeEx(FileList[i], '\', '/',True). As soon as I put that in, the idp.iss stops compiling, giving me the error: Variable expected on
procedure idpAddFile(url, filename: String); external 'idpAddFile#files:idp.dll cdecl';
Installer compiles normal, if I remove StringChangeEx from my script entirely. It does not help to place it in a different location...
Any idea, what might cause this problem?
The StringChangeEx function needs a string variable in the first argument (it's declared as var). You are giving it a string value only.
Your code would work, were FileList a string array. But it's not, it's a class with an array-like string default property. Using a property value is an equivalent of using a function/method return value. A property is just syntactic sugar for getter and setter methods.
You will have to copy the value to a string variable and back.
S := FileList[i];
StringChangeEx(S, '\', '/',True);
FileList[i] := S;
Though you actually do not need to copy it back in your case.
Regarding the reason why the error refers to idp.iss file: There seems to be a bug in Inno Setup that makes it report the error as if it had occurred on the very first line of the Pascal Script code (what in your case is the very first real code in the included idp.iss). I've posted a bug report. It was fixed already.
I'm creating a program in Ruby for replacing content of multiple files in one directory, and there are four arguments:
dir for defining directory
ext for defining extension
find for defining string to find
replace for defining string that replace string defined as find
Right now, this is start of the program:
dir, ext, find, replace = ARGV
dir, ext, find, replace = ARGV.shift
raise "Missing argument for directory" unless dir
raise "..." unless ext
#etc
However, when I run this program without defining arguments, it shows only first argument that is not defined (directory) with RuntimeError and imediatelly closes program. Is there any different approach for this?
I can offer you such variant:
missing_parameters = ["dir","ext","find","replace"].select{|name| eval(name).nil?}
raise "Missing parameters for #{missing_parameters.join(',')}" unless missing_parameters.empty?
But frankly speaking, i don't like the usage of eval :)
Is it possible not to set default value in flag package in Go? For example, in flag package you can write out the following line:
filename := flag.String("file", "test.csv", "Filename to cope with")
In the above code, I don't want to necessarily set default value, which is test.csv in this case, and instead always make users specify their own filename, and if it's not specified then I want to cause an error and exit the program.
One of the way I came up with is that I first check the value of filename after doing flag.Parse(), and if that value is test.csv then I have the program exits with the appropriate error message. However, I don't want to write such redundant code if it can be evaded - and even if it can't, I'd like to hear any better way to cope with the issue here.
You can do those kind of operations in Python's argparse module by the way - I just want to implement the similar thing if I can...
Also, can I implement both short and long arguments (in other words both -f and -file argument?) in flag package?
Thanks.
I think it's idiomatic to design your flag values in such a way which implies "not present" when equal to the zero value of their respective types. For example:
optFile := flag.String("file", "", "Source file")
flag.Parse()
fn := *optFile
if fn == "" {
fn = "/dev/stdin"
}
f, err := os.Open(fn)
...
Ad the 2nd question: IINM, the flag package by design doesn't distinguish between -flag and --flag. IOW, you can have both -f and --file in your flag set and write any version of - or -- before both f and file. However, considering another defined flag -g, the flag package will not recognize -gf foo as being equvalent of -g -f foo.
When I have a flag that cannot have a default value I often use the value REQUIRED or something similar. I find this makes the --help easier to read.
As for why it wasn't baked in, I suspect it just wasn't considered important enough. The default wouldn't fit every need. However, the --help flag is similar; it doesn't fit every need, but it's good enough most of the time.
That's not to say the required flags are a bad idea. If you're passionate enough a flagutil package could be nice. Wrap the current flag api, make Parse return an error that describes the missing flag and add a RequiredInt and RequiredIntVar etc. If it turns out to be useful / popular it could be merged with the official flag package.
This is how I implemented command argument parser.
Since there are already plenty of similar projects, I decided not to add more choices without strong impetus.
Here is an example of how it can be used, which might inspired somebody, or someone might be interested.
# minarg.go
package main
import (
"fmt"
"self/argp"
)
func main() {
p := argp.New(nil)
p.Bool("continue",nil,"-v","-g")
f := func(m, arg string) {
switch m {
case "__init__":
case "__defer__":
p.Set("server", p.GetString("-s") + ":" + p.GetString("-p"))
default:
arg, _ := p.Shift()
p.Set(m, arg)
}
}
p.Mode(f,"__init__","__defer__","-s","-p","-nstat","-n")
p.Default("-s","127.0.0.1", "-p","1080", "-nstat","100", "-n","5")
p.Env("-s","SERVER", "-p","PORT")
p.Parse()
fmt.Println(p.Vars)
}
The output is
$ go run minarg.go
&map[-g:{false continue <nil>} -n:5 -nstat:100 -p:1080 -s:127.0.0.1 -v:{false continue <nil>} server:127.0.0.1:1080]
$ export PORT=80
$ go run minarg.go -s 0.0.0.0 -n 3 -vg
&map[-g:{true continue <nil>} -n:3 -nstat:100 -p:80 -s:0.0.0.0 -v:{true continue <nil>} server:0.0.0.0:80]
I have a problem with the GetCommandLine() API.
It usually returns the executable name followed by a space and arguments. As documentation says, the first token may not have the complete path to the image and blah blah blah.
I never had problems until now that I used CreateProcess with lpApplicationName not NULL.
If I use:
CreateProcess(NULL, "\"c:\\myexe.exe\" param1 param2", ...)
GetCommandLine returns "c:\myexe.exe param1 param2" as expected.
But if I use:
CreateProcess("C:\myexe.exe", "param1 param2")
GetCommandLine returns only "param1 param2".
How do I know if the executable name is given on the command line if another application launches mine?
Also, MFC startup code assumes that the first token on the command line is the executable name and skips it. But if you launch a MFC application with the second CreateProcess API example, MFC's code will skip the first argument.
Not your problem. It's the job of the other application to construct the command line properly. You should simply assume that the first argument is an executable name as expected and skip over it.
I have a workaround which can be helpful in a case like this.
I guess we always be able to check how our module was been started.
In this case we should check first argument.
I will write code because I have some problem with English.
Here two ways:
The first case. we can compare module name with first command line argument.
something like this:
const TCHAR* csCommandLine = ::GetCommandLine();
// Attention!!! the first symbol can be quete
if (*csCommandLine == _T('\"'))
csCommandLine++;
TCHAR sModuleFileName[MAX_PATH];
DWORD dwModuleFileName = ::GetModuleFileName(NULL, sModuleFileName, MAX_PATH);
if (dwModuleFileName && !_tcsncmp(csCommandLine, sModuleFileName, dwModuleFileName)) {
// The command line contains the module name.
}
The second case. we can try to get file attributes for the first command line argument
something like this:
// Attention!!! don't use it case if you are going to pass a file path in command line arguments.
int nArgc;
LPTSTR* szArglist = ::CommandLineToArgvW(::GetCommandLine(), &nArgc);
if (nArgc && ::GetFileAttributes(szArglist[0]) != INVALID_FILE_ATTRIBUTES) {
// The command line contains the module name.
}
::LocalFree(szArglist);
I hope it can be helpful someone.
Regards, Vladimir