Home > Blockchain >  Batch one line to call an executable using arguments from file
Batch one line to call an executable using arguments from file

Time:02-14

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.

  • Related