2

For some back info to help you guys out, I'm making a small program that will take an .SQL file and restore it to the users localhost phpMyAdmin using mysql.exe. Importing the file through the MyAdmin itself by hand takes forever as it has to unzip and parse the information, this method is quicker and it's for my coworkers as we're always pushing backups and restores to our WAMP to experiment with.

My one issue I'm facing is that I can't seem to get it to change directories no matter what. I understand that wherever the executable was launched is the path that the system will inherit, so why can't I simply tell it to change directories?

Here's my code so far:

system("cd c:\wamp64\bin\mysql\mysql5.7.14\bin");
system("PAUSE");
system("dir");
system("PAUSE");
system("mysql.exe -u root -p dataBase < tables.sql");

Note: The pauses and system("dir") are in there strictly for testing purposes, once the window spits back the correct contents of the directory, then I know that it works and I'll only have the first and last lines left in the program.

UPDATE: I've been notified that every call to system creates a new CMD process which makes perfect sense as to why the cd will not effect anything, as it's opening a new process, changing directories, then on the next line it's calling a new process and pausing that. What would be the workaround for this?

UPDATE 2: I decided to join multiple commands together using && but that limits me on the usability and "styling" side of things, but that's not a problem, the program is needed as a run & done type of deal.

Here's what I've come up with:

system("cd c:\\wamp64\\bin\\mysql\\mysql5.7.14\\bin && mysql.exe -u root -p dataBase < table.sql");
cout << "\n" << "The database 'dataBase' has been restored using 'table.sql' successfully!" << "\n\n";
return 0;

Are there any advantages to creating a .cmd file and running it using CreateProcess() or disadvantages to using the method that I've come up with?

Note: The database name and .sql files aren't pulled from anything, the names will be static so I don't have to worry about updating that.

Brxxn
  • 112
  • 1
  • 12
  • https://msdn.microsoft.com/en-us/library/windows/desktop/aa365530 – SLaks Jun 14 '17 at 20:38
  • 3
    *Please* don't use `system`. It has so, so, many issues... – Jesper Juhl Jun 14 '17 at 20:41
  • 1
    1. Didn't you get a warning about bad escape sequences? 2. each call to system opens a new command session.As a result the cd has no effect. – drescherjm Jun 14 '17 at 20:42
  • ***What would be the workaround for this?*** Do everything in one system() call. – drescherjm Jun 14 '17 at 20:48
  • Lookup && in your cmd.exe reference. – drescherjm Jun 14 '17 at 20:49
  • You could concat the commands by ``&`` into one string or just put the code into a (temporary) batch file and run it with ``system``. – cmdLP Jun 14 '17 at 20:51
  • Why would you need the `cd` call at all when you could just use **absolute** pathes for everything? Like `system("\"c:\\wamp64\\bin\\mysql\\mysql5.7.14\\bin\\mysql.exe\" -u root -p dataBase < \"c:\\wamp64\\bin\\mysql\\mysql5.7.14\\bin\\table.sql\"")` – zett42 Jun 14 '17 at 21:22
  • @JesperJuhl Please elaborate on the issues of `system`. – zett42 Jun 14 '17 at 21:23
  • @zett42 in a nutshell: the child process inherits the parents permissions which may be too broad for what it needs to do. You have no control over what shell will be used to run the command. You have no control over the environment used for the child. You have very limited ways of communicating with the child (basically you just get its exit code). There are more issues, but that should be enough to get you to shy away from it. – Jesper Juhl Jun 14 '17 at 21:30
  • @Jesper I don't think these are actual *issues*. An issue is something that may turn into a *problem*. This is more a matter of choosing the API that provides only those features that you really need. When you call `CreateProcess()` as in the accepted answer there isn't much difference to the `system()` call. – zett42 Jun 14 '17 at 21:40

2 Answers2

3

A better idea is to dynamically generate a batch file (.cmd or .bat, there are slight differences) and then invoke that with cmd.exe /c batchFile.cmd:

#include <cstdlib>
#include <cstdio>

ofstream batch_file;
batch_file.open( "commands.cmd", ios::trunc );
batch_file <<
    "cd c:\\wamp64\\bin\\mysql\\mysql5.7.14\\bin" << endl <<
    "PAUSE" << endl <<
    "dir" << endl <<
    "PAUSE" << endl <<
    "mysql.exe -u root -p dataBase < tables.sql" << endl;
batch_file.close();

int batch_exit_code = system( "cmd.exe /c commands.cmd" ); // blocks until the child process is terminated
if( batch_exit_code != 0 ) {
    cout << "Batch file exited with code " << batch_exit_code << endl;
}

remove( "commands.cmd" ); // delete the batch file

This has the benefit of allowing you to customize the text and commands, such as performing your own detection of the location of the MySQL binaries and your temporary files.

Note that if you're always running on Windows, you might want to use CreateProcess instead, as system has some overheads:

system() and CreateProcess() / CreateProcessW()

The system function passes command to the command interpreter, which executes the string as an operating-system command. system refers to the COMSPEC and PATH environment variables that locate the command-interpreter file (the file named CMD.EXE in Windows NT).

As you want to run cmd.exe directly, this would result in it opening twice.

You can use CreateProcess instead:

STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;
if( CreateProcess( "cmd.exe", "/c commands.cmd", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo ) )
{
    WaitForSingleObject( processInfo.hProcess, INFINITE );
    CloseHandle( processInfo.hProcess );
    CloseHandle( processInfo.hThread );
}
Dai
  • 110,988
  • 21
  • 188
  • 277
  • Excellent information! I read up on the CreateProcess, seems like a more viable option over calling the system to open, well, the system. Now wouldn't this cause problems as that command.cmd file will always remain? Or will every iteration simply overwrite the previous one? – Brxxn Jun 14 '17 at 21:06
  • @Brian I amended my code to delete the file after the file is executed. – Dai Jun 14 '17 at 21:06
  • Thank you so much man! You guys are amazing, 15 minutes in and I've got more information than I literally asked for! I'll do my best to help others out where I can. Back on topic, I've updated my original post, are there any upsides to using CreateProcess() over my single call to system? – Brxxn Jun 14 '17 at 21:21
  • 1
    2nd code sample doesn't work at all. See the description of `CreateProcess()` parameter `lpApplicationName`: _In the case of a partial name, the function uses the current drive and current directory to complete the specification. The function will not use the search path._ – zett42 Jun 14 '17 at 22:01
2

Here is a way you can execute system with directory change:

// using AND_CMD like an operator to concat commands -- you can also just type it in one string: "edit & shutdown -h"...
// this works in c, too, but no dynamic variables are possible
//#define AND_CMD " & "

// c++
#define AND_CMD +" & "+

system(
    "cd c:\wamp64\bin\mysql\mysql5.7.14\bin" AND_CMD
    "PAUSE" AND_CMD
    "dir" AND_CMD
    "PAUSE" AND_CMD
    "mysql.exe -u root -p dataBase < tables.sql"
);

So you only need to join the commands with the string " & ". If you need to run the code on conditions to the program itsself/keep the directory by next time calling the system-function, you should look for alternatives or you pipe the commands into the commandprompt (eg. running your program by myprogram.exe | %comspec% and invoking the commands by outputting with eg. printf.

cmdLP
  • 1,375
  • 7
  • 17