-3

I am a beginner in learning C++ and I have a lot of experience in making batch files. I found the command 'system("command");' which preforms a single CMD command. There is one problem with it which is that we cannot declare a variable.

lets say we have this code in a batch file:

@echo off
set x= hi
echo %x%
pause

the above code will provide the message hi and pause.

when i tried to rewrite it in C++, it will look something like

int main()
{
    system("set x=hi");
    system("echo %x%");
    system("pause");
    return 0;
}

this will only will print %x% instead of hi. How can I declare a variable and use it? I thought maybe there is a way to write multiple commands in one system(""); but i don't know how. Thanks

Edit: For those who are curious why I want to do this, I want to convert some of the programs i made to work as a part of c++ program. I've worked on batch files for years and I made many many programs. It will be helpful if i can implement them here.

Evil DEvil
  • 40
  • 6
  • 11
    Why exactly do you want to do this? Writing batch commands in C++ is often times the wrong approach to problems. – UnholySheep May 24 '18 at 11:41
  • 2
    You're setting variable value in a temporary child process (in a `cmd` that is spawned for every `system()` call). I think you need to use `setx` instead -- it will modify global variable value, not a private value of temporary child. More info can be found here: https://stackoverflow.com/questions/3803581/setting-a-system-environment-variable-from-a-windows-batch-file – alx May 24 '18 at 11:45
  • @UnholySheep I want to convert some of the programs i made to work as a part of c++ program. I've worked on batch files for years and I made many many programs. It will be helpful if i can implement them here. – Evil DEvil May 24 '18 at 12:47
  • This is an interesting, non-trivial question. It is quite probable that fundamentally different approaches are better suited to solve the OP's actual (unknown) problem, but the question as it stands is perfectly valid. – Peter - Reinstate Monica May 24 '18 at 12:48
  • @EvilDEvil It's likely that you are better off with some scripting language if you want to "glue" existing batch files together. And consider calling batch scripts *as files* as I suggested in my edited answer, rather than calling `system` with real commands. Data which you would hold in DOS variables could come from command line arguments to your (C++ or other) program. – Peter - Reinstate Monica May 24 '18 at 12:51
  • @EvilDEvil By the way, it is better to add additional information to the question proper, not as comments. – Peter - Reinstate Monica May 24 '18 at 12:54
  • You have a different definition of "convert" than me - for me "converting" code would imply that I rewrite it from scratch using the facilities and paradigms of the new language. You are trying to "glue" code written in one language into a different language (which is something I would not recommend except in very, very specific scenarios) – UnholySheep May 24 '18 at 12:57
  • 1
    You forgot a basic point in your question. If you have any code _in a Batch file_, you did _NOT_ execute such a code _via the commands themselves_; you just execute **the Batch file**! If you want to do the same in C++ just _create a Batch file_ and then execute it via `system` call. If you really want to solve this problem in this way, you need to find a way to execute several individual Batch commands with no Batch file and not in a `cmd.exe` session. After that, just use the same method in your C++ program... **`:/`** – Aacini May 24 '18 at 13:35

2 Answers2

0

You start a new command shell with every system call; variables you declare exist only in that command shell.

Unfortunately I was not able to find out how to use variables in system calls directly.1

Furtunately though, @aschipfl knew the answer and posted it as comment (thanks!). The reason set a=abc && echo %a% does not work (also not when you type it in a console) is that %a% is evaluated when the command is parsed, that is, before the set command has been executed. At that point the variable is not set. Calling cmd with "delayed expansion" enabled switches the evaluation time to execution time; when the echo is encountered the variable is defined and will be printed.

The option to activate delayed evaluation is /V. cmd /? prints the following:

/V:ON Enable delayed environment variable expansion using ! as the delimiter. For example, /V:ON would allow !var! to expand the variable var at execution time. The var syntax expands variables at input time, which is quite a different thing when inside of a FOR loop.

So this will work:

#include <cstdlib>

using namespace std;

int main()
{ 
  system("cmd /V:ON /C set a=abcd ^&^& echo !a!");
  return 0;
}

Another solution could be to let your program create a temporary batch file and then execute that file in a single trivial system command. The strategy to move complicated scripts in files is recommended in general for Windows because it avoids command line parsing by cmd which is notoriously hard to get right (here is a Microsoft blog diving into the details).

#include <cstdlib>
#include <fstream>

using namespace std;

int main()
{
  ofstream batchfile{ "tmpbatchfile.bat" };

  batchfile << "set x=abc\n";
  batchfile << "echo %x%\n";
  batchfile << "pause\n";

  system("tmpbatchfile.bat");

  return 0;
}

Note that the system call appears to return before the command has finished. This makes it impossible to delete the temporary file.


1One should think that passing a string with more than one command — multi-line or &&-chained — would do the trick, but I cannot get it to work.

For example system("echo abc && echo ****"); works; but system("set a=abc && echo %a%"); does not (it prints a literal %a%).

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
-1

I thought maybe there is a way to write multiple commands in one system(""); but i don't know how.

The string passed to system will get passed on to the underlying shell as a single command. It is implementation-defined (and highly system-specific) what behavior that string will trigger on the underlying shell.

In your concrete case of invoking the standard cmd shell on Windows, it is not possible to reproduce the exact behavior of your example batch script with a simple system call.

Unlike a batch script, where each command gets executed in the same interpreter context, the three distinct system calls from your second example each create a new context. So by the time you execute the second system call, the state set by the first call (ie. the value of the environment variable x) has already been lost.

Unfortunately, it is also not possible to directly combine arbitrary batch commands into a single system call.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166