Closed Deozaan closed 5 years ago
So, first of all: the shell is responsible for splitting arguments and passing them to applications, butler just uses that.
Second, I'm confused which shell you're using currently, because neither bash nor cmd.exe behave like that.
In bash, single quotes are valid, if we take this test script:
#!/bin/bash
echo "Argument 1 = ($1)"
echo "Argument 2 = ($2)"
echo "Argument 3 = ($3)"
We get:
$ ./test.sh '.\Game Dir\' username/game:win32
Argument 1 = (.\Game Dir\)
Argument 2 = (username/game:win32)
Argument 3 = ()
The only way to reproduce the output you're seeing would be to move the backslash after the single quote, in which case it would act as an escape for the space, and we'd see:
$ ./test.sh '.\Game Dir'\ username/game:win32
Argument 1 = (.\Game Dir username/game:win32)
Argument 2 = ()
Argument 3 = ()
... which is consistent with the error message you're posting.
In cmd.exe, single quotes don't have any special meaning, so your whole command is just space-separated.
If we take test.bat:
@echo Argument 1 = (%1)
@echo Argument 2 = (%2)
@echo Argument 3 = (%3)
We get:
> test.bat '.\Game Dir\' username/game:win32
Argument 1 = ('.\Game)
Argument 2 = (Dir\')
Argument 3 = (username/game:win32)
Which would give a different butler error message:
butler: error: unexpected username/game:win32
..which is also correct, because it expects two arguments, not three.
Since I don't believe you're making up bug reports, I tried PowerShell, and it does behave as you've posted.
The test.bat gives this:
> .\test.bat '.\Game Dir\' username/game:win32
Argument 1 = (".\Game Dir\")
Argument 2 = (username/game:win32)
Argument 3 = ()
And a sample test.c
, like so:
#include <stdio.h>
int main(int argc, char **argv) {
for (int i = 0; i < argc; i++) {
printf("Argument %d = (%s)\n", i, argv[i]);
}
return 0;
}
Gives us this:
> .\test '.\Game Dir\' username/game:win32
Argument 0 = (C:\msys64\home\amos\Dev\butler\test.exe)
Argument 1 = (.\Game Dir" username/game:win32)
Which is interesting. Additionally, if I change butler's main function to print its arguments, like this:
func main() {
for i, arg := range os.Args {
log.Printf("Arg %d: %q", i, arg)
}
doMain(os.Args[1:])
}
Then we see:
> butler push '.\Game Dir\' username/game:win32
2019/05/02 13:10:14 Arg 0: "C:\\Users\\amos\\go\\bin\\butler.exe"
2019/05/02 13:10:14 Arg 1: "push"
2019/05/02 13:10:14 Arg 2: ".\\Game Dir\" username/game:win32"
butler: error: required argument 'target' not provided
Which also matches my expectations.
It has been mentioned before - cmd.exe
has different rules for "tokenizing" arguments than CommandLineToArgvW, which is what pretty much all native programs use.
If you pass 'a\' b
to a native program, it'll receive "a\" b
as a single argument.
This isn't a bug in butler, or Go, or windows. It's just cmd.exe/PowerShell having silly rules. The fix is to simply not have a trailing backslash at the end of your arguments. I wish it was my fault so that I could fix it, but it's not, and adding workarounds toe butler's codebase would make the situation even more confusing! (as it would behave differently from every other native program out there).
Yes, I was using PowerShell. Thanks for the investigative work, and the explanation.
I'll keep in mind to avoid the trailing backslash at the end of my path.
When using the default Windows path separators (backslash) to push with butler, I get an error saying:
butler: error: required argument 'target' not provided
Changing the backslashes to forward slashes in the path allows things to push properly.
It seems that butler doesn't understand that strings surrounded in single-quotes should be interpreted literally and not be escaped.
Example: