For convenience, I have renamed all the files to simple names for my example.
I'm trying to run an executable (test.exe), with a C entrypoint int main(int argc, char* argv[])
from a batch file (test.bat), and pass arguments from a text file (test.txt). The end goal is to run unit tests on an SDK using the testing software (test.exe).
My issue is that I do not want to have to use a variable when I call the executable since it makes the code harder to read :
rem This works
set /p test_input=<test.txt& call test.exe %test_input%
After some research, I figured I should use input redirection like so :
rem This does not work
call test.exe < test.txt
This does not work, and I don't understand why.
This is what I initially tried, and it has been suggested before on SO (here). I have access to the test.exe code, so I can print argc and argv :
int main(int argc, char* argv[])
{
if(new_argc >= 2)
{
if(strcmp("-help", argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < argc; i )
{
if(strcmp("-framerate", argv[i]) ==0)
{
i ;
if(i < argc)
{
FrameRate = (float)atof(argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
}
If I enter the arguments and parameters manually, it works as expected.
test.txt
-arg1 param1 -arg2 param2 ...
test.bat
call test.exe < test.txt
Output : test.exe runs as if there are no arguments or parameters.
Edit : Added a few details about the entrypoint and renamed the batch variable.
CodePudding user response:
Thanks to the comments under my question, I was pushed in the right direction.
The problem was my understanding of <
. It literally means "Read file to STDIN" (as mentionned here). Many other documentation sites give vague definitions like (as mentionned here)
command < filename : Type a text file and pass the text to command
I need to parse the input correctly, since stdin
isn't available in argc
or argv
, but through std::cin
.
My batch code and text file remain unchanged, and I want to maintain the same form of parsing to avoid rewriting multiple projects, so I split
the input string using the Solution 1.3 from here (slightly modified) and created a new_argv
.
std::vector<char*> split(const std::string& s, char delimiter)
{
std::vector<char*> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter))
{
tokens.push_back(_strdup(token.c_str()));
}
return tokens;
}
int main(int argc, char* argv[])
{
std::string extra_input; // Variable to store the contents of test.txt
std::getline(std::cin, extra_input); // Recuperate the contents of test.txt
std::vector<char*> new_argv = split(extra_input, ' '); // Split the args
for(int i = argc - 1; i >= 0; i--)
new_argv.insert(new_argv.begin(), argv[i]); // Add the original args to the beginning
const size_t new_argc = new_argv.size(); // Create the new argc based on the final argument list (vector)
if(new_argc >= 2)
{
if(strcmp("-help", new_argv[1]) ==0)
{
show_help();
return 0;
}
for(int i=1; i < new_argc; i )
{
if(strcmp("-framerate", new_argv[i]) ==0)
{
i ;
if(i < new_argc)
{
FrameRate = (float)atof(new_argv[i]);
}
else
{
std::cerr << "Parameters error" << std::endl;
return 0;
}
} else if ...
{
...
}
}
}
// Important, don't forget to free the memory used by the _strdup
for(int i=1; i < new_argc; i )
{
if(i >= argc)
free(new_argv[i]);
}
}
test.bat
call test.exe < test.txt
test.txt
-arg1 param1 -arg2 param2 ...
Of course, I need to add some checks to make it properly handle whitespace, but that's the gist of it. Thank you for your help and external point of view.
Edit : Fixed a mistake in the code.