How to use a CMD command in c++? - c++

I want to use this cmd command
ROBOCOPY D:\folder1 D:\folder2 /S /E
with conditions to copy the contents of folder1 to folder2
if(i == 1)
and,
if(i == 2)
ROBOCOPY D:\folder3 D:\folder4 /S /E
to copy the contents of folder3 to folder4
what should i do?

"what should i do?"
You simply do this (using the std::system() function):
#include <cstdlib>
// ...
if(i == 1) {
std::system("ROBOCOPY D:/folder1 D:/folder2 /S /E");
}
else if(i == 2) {
std::system("ROBOCOPY D:/folder3 D:/folder4 /S /E");
}
Note that for string literals like "D:\folder3", you'll need to escape '\' characters, with another '\': "D:\\folder3".
Or even two more, depending on the interpreting command shell (should work on windows cmd without doing so): "D:\\\\folder3".
The easier way though, is to use the simpler to write '/' character, that's accepted for specifying windows pathes lately as well.

The simplest way is to call the standard library function system: http://www.cplusplus.com/reference/cstdlib/system/
If you need more flexibility, http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx CreateProcess is the thing to go for - the STARTUPINFO argument lets you do things like pass it custom input and capture its output too.

Related

If command in Batch file

I am wondering why my code works this way:
If exist c:\work\first\food.txt (echo win) else (echo fail)
but not like this:
If exist c:\work\first\food.txt (echo food.txt is in C:\work\first) else (echo fail)
I'm using Notepad++ to write this and "in" and c:\work\first are highlighted blue like the other commands if that helps.
Your code works fine. There is no need to escape anything in that particular line.
However double quotes are needed in several cases - where spaces are in the path or & characters etc.
If exist "c:\work\first\food.txt" (echo food.txt is in C:\work\first) else (echo fail)

system("pause") clarification

When i use system("pause"), then a line "Press any key to continue..." shows up on the screen.
This is iritating and makes reading the output quite cumbersome.
Is there some way to stop this from coming?
Do you mean that you want to press any key to continue but not to display the "Press any key to continue" on the screen? Try this getchar(); this will capture one character typing from keyboard and continue.
Rather than using platform dependent system("pause") you can use the platform independent std::cin.get() and if the buffer is messing with it, you can use:
std::cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n')
before hand to clear the buffer.
Assuming you're on Windows, replace the system("pause") with system("pause > NULL").
First of all, you should never use system("pause") because it is dangerous. Your code will be calling an external system procedure for no reason; and a cracker can find a way to substitute the "pause" command to other, making your program call the other program with your user permissions.
That said, you can avoid the message sending it to null device.
On Windows:
pause > nul
And if you want to be bold to make this awful system call portable, you can use:
on linux:
echo Press any key to continue ...; read x
Now you can apply the OR and AND (logic connectives) to both and make a system call that works on both systems:
void pause(void)
{
system("echo Press any key to continue . . . && ( read x 2> nul; rm nul || pause > nul )");
return;
}
Linux will create a temporary file called "nul" because it does not recognize this keyword. The null device on linux is /dev/null, not just nul. After that, the command will remove this temporary file with rm nul. So if you happen to have a file named nul on the same directory, be warned this command is not good for you (for yet another reason).
This command mimics the original. If you want to avoid the message, just remove the echo Pres... part of it.
Bonus:
Clear the terminal screen portably using system? (No, do not do this for the same reasons. Its dangerous.) But for tests purposes, you can use:
system("cls||clear");
Avoid pause. C is a language, one of the most powerful languages that there is. I'm sure there is a way to make a pause using only C (getchar() or scanf() for instance).
That line is part of the system("pause"). You can try a different method, such as getline(std::cin, variable) or cin.get().
Use
system("pause>nul")
It works perfectly for windows!

C++ system() not working when there are spaces in two different parameters

I'm trying to run a .exe that requires some parameters by using system().
If there's a space in the .exe's path AND in the path of a file passed in parameters, I get the following error:
The filename, directory name, or volume label syntax is incorrect.
Here is the code that generates that error:
#include <stdlib.h>
#include <conio.h>
int main (){
system("\"C:\\Users\\Adam\\Desktop\\pdftotext\" -layout \"C:\\Users\\Adam\\Desktop\\week 4.pdf\"");
_getch();
}
If the "pdftotext"'s path doesn't use quotation marks (I need them because sometimes the directory will have spaces), everything works fine. Also, if I put what's in "system()" in a string and output it and I copy it in an actual command window, it works.
I thought that maybe I could chain some commands using something like this:
cd C:\Users\Adam\Desktop;
pdftotext -layout "week 4.pdf"
So I would already be in the correct directory, but I don't know how to use multiple commands in the same system() function.
Can anyone tell me why my command doesn't work or if the second way I thought about would work?
Edit: Looks like I needed an extra set of quotation marks because system() passes its arguments to cmd /k, so it needs to be in quotations. I found it here:
C++: How to make a my program open a .exe with optional args
so I'll vote to close as duplicate since the questions are pretty close even though we weren't getting the same error message, thanks!
system() runs command as cmd /C command. And here's citation from cmd doc:
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
It seems that you are hitting case 2, and cmd thinks that the whole string C:\Users\Adam\Desktop\pdftotext" -layout "C:\Users\Adam\Desktop\week 4.pdf (i.e. without the first and the last quote) is the name of executable.
So the solution would be to wrap the whole command in extra quotes:
//system("\"D:\\test\" nospaces \"text with spaces\"");//gives same error as you're getting
system("\"\"D:\\test\" nospaces \"text with spaces\"\""); //ok, works
And this is very weird. I think it's also a good idea to add /S just to make sure it will always parse the string by the case 2:
system("cmd /S /C \"\"D:\\test\" nospaces \"text with spaces\"\""); //also works
I got here looking for an answer, and this is the code that I came up with (and I was this explicit for the benefit of next person maintaining my code):
std::stringstream ss;
std::string pathOfCommand;
std::string pathOfInputFile;
// some code to set values for paths
ss << "\""; // command opening quote
ss << "\"" << pathOfCommand << "\" "; // Quoted binary (could have spaces)
ss << "\"" << pathOfInputFile << "\""; // Quoted input (could have spaces)
ss << "\""; // command closing quote
system( ss.str().c_str() ); // Execute the command
and it solved all of my problems.
Good learning from here on the internals of System call.Same issue reproducible(of course) with C++ string, TCHARs etc.
One approach that always helped me is SetCurrentDirectory() call. I first set current path and then execute. This has worked for me so far. Any comments welcome.
-Sreejith. D. Menon

Can I have an IF block in DOS batch file?

In a DOS batch file we can only have 1 line if statement body? I think I found somewhere that I could use () for an if block just like the {} used in C-like programming languages, but it is not executing the statements when I try this. No error message either. This my code:
if %GPMANAGER_FOUND%==true(echo GP Manager is up
goto Continue7
)
echo GP Manager is down
:Continue7
Strangely neither "GP Manager is up" nor "GP Manager is down" gets printed when I run the batch file.
You can indeed place create a block of statements to execute after a conditional. But you have the syntax wrong. The parentheses must be used exactly as shown:
if <statement> (
do something
) else (
do something else
)
However, I do not believe that there is any built-in syntax for else-if statements. You will unfortunately need to create nested blocks of if statements to handle that.
Secondly, that %GPMANAGER_FOUND% == true test looks mighty suspicious to me. I don't know what the environment variable is set to or how you're setting it, but I very much doubt that the code you've shown will produce the result you're looking for.
The following sample code works fine for me:
#echo off
if ERRORLEVEL == 0 (
echo GP Manager is up
goto Continue7
)
echo GP Manager is down
:Continue7
Please note a few specific details about my sample code:
The space added between the end of the conditional statement, and the opening parenthesis.
I am setting #echo off to keep from seeing all of the statements printed to the console as they execute, and instead just see the output of those that specifically begin with echo.
I'm using the built-in ERRORLEVEL variable just as a test. Read more here
Logically, Cody's answer should work. However I don't think the command prompt handles a code block logically. For the life of me I can't get that to work properly with any more than a single command within the block. In my case, extensive testing revealed that all of the commands within the block are being cached, and executed simultaneously at the end of the block. This of course doesn't yield the expected results. Here is an oversimplified example:
if %ERRORLEVEL%==0 (
set var1=blue
set var2=cheese
set var3=%var1%_%var2%
)
This should provide var3 with the following value:
blue_cheese
but instead yields:
_
because all 3 commands are cached and executed simultaneously upon exiting the code block.
I was able to overcome this problem by re-writing the if block to only execute one command - goto - and adding a few labels. Its clunky, and I don't much like it, but at least it works.
if %ERRORLEVEL%==0 goto :error0
goto :endif
:error0
set var1=blue
set var2=cheese
set var3=%var1%_%var2%
:endif
Instead of this goto mess, try using the ampersand & or double ampersand && (conditional to errorlevel 0) as command separators.
I fixed a script snippet with this trick, to summarize, I have three batch files, one which calls the other two after having found which letters the external backup drives have been assigned. I leave the first file on the primary external drive so the calls to its backup routine worked fine, but the calls to the second one required an active drive change. The code below shows how I fixed it:
for %%b in (d e f g h i j k l m n o p q r s t u v w x y z) DO (
if exist "%%b:\Backup.cmd" %%b: & CALL "%%b:\Backup.cmd"
)
I ran across this article in the results returned by a search related to the IF command in a batch file, and I couldn't resist the opportunity to correct the misconception that IF blocks are limited to single commands. Following is a portion of a production Windows NT command script that runs daily on the machine on which I am composing this reply.
if "%COPYTOOL%" equ "R" (
WWLOGGER.exe "%APPDATA%\WizardWrx\%~n0.LOG" "Using RoboCopy to make a backup of %USERPROFILE%\My Documents\Outlook Files\*"
%TOOLPATH% %SRCEPATH% %DESTPATH% /copyall %RCLOGSTR% /m /np /r:0 /tee
C:\BIN\ExitCodeMapper.exe C:\BIN\ExitCodeMapper.INI[Robocopy] %TEMP%\%~n0.TMP %ERRORLEVEL%
) else (
WWLOGGER.exe "%APPDATA%\WizardWrx\%~n0.LOG" "Using XCopy to make a backup of %USERPROFILE%\My Documents\Outlook Files\*"
call %TOOLPATH% "%USERPROFILE%\My Documents\Outlook Files\*" "%USERPROFILE%\My Documents\Outlook Files\_backups" /f /m /v /y
C:\BIN\ExitCodeMapper.exe C:\BIN\ExitCodeMapper.INI[Xcopy] %TEMP%\%~n0.TMP %ERRORLEVEL%
)
Perhaps blocks of two or more lines applies exclusively to Windows NT command scripts (.CMD files), because a search of the production scripts directory of an application that is restricted to old school batch (.BAT) files, revealed only one-command blocks. Since the application has gone into extended maintenance (meaning that I am not actively involved in supporting it), I can't say whether that is because I didn't need more than one line, or that I couldn't make them work.
Regardless, if the latter is true, there is a simple workaround; move the multiple lines into either a separate batch file or a batch file subroutine. I know that the latter works in both kinds of scripts.
Maybe a bit late, but hope it hellps:
#echo off
if %ERRORLEVEL% == 0 (
msg * 1st line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue1
)
:Continue1
If exist "C:\Python31" (
msg * 2nd line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue2
)
:Continue2
If exist "C:\Python31\Lib\site-packages\PyQt4" (
msg * 3th line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue3
)
:Continue3
msg * 4th line WORKS FINE rem You can relpace msg * with any othe operation...
goto Continue4
)
:Continue4
msg * "Tutto a posto" rem You can relpace msg * with any othe operation...
pause

C++: How to escape user input for safe system calls?

On a Linux platform, I have C++ code that goes like this:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
std::string command;
command = "mkdir " + myDir;
if (system(command.c_str()) != 0) {
return 1;
}
// continue....
Is passing user input to a system() call safe at all?
Should the user input be escaped / sanitized?
How?
How could the above code be exploited for malicious purposes?
Thanks.
Just don't use system. Prefer execl.
execl ("/bin/mkdir", "mkdir", myDir, (char *)0);
That way, myDir is always passed as a single argument to mkdir, and the shell isn't involved. Note that you need to fork if you use this method.
But if this is not just an example, you should use the mkdir C function:
mkdir(myDir, someMode);
Using system() call with command line parameters without sanitizing the input can be highly insecure.
The potential security threat could be a user passing the following as directory name
somedir ; rm -rf /
To prevent this , use a mixture of the following
use getopt to ensure your input is
sanitized
sanitize the input
use execl instead of system to execute
the command
The best option would be to use all three
Further to Matthew's answer, don't spawn a shell process unless you absolutely need it. If you use a fork/execl combination, individual parameters will never be parsed so don't need to be escaped. Beware of null characters however which will still prematurely terminate the parameter (this is not a security problem in some cases).
I assume mkdir is just an example, as mkdir can trivially be called from C++ much more easily than these subprocess suggestions.
Reviving this ancient question as I ran into the same problem and the top answers, based on fork() + execl(), weren't working for me. (They create a separate process, whereas I wanted to use async to launch the command in a thread and have the system call stay in-process to share state more easily.) So I'll give an alternative solution.
It's not usually safe to pass user input as-is, especially if the utility is designed to be sudo'd; in order to sanitize it, instead of composing the string to be executed yourself, use environment variables, which the shell has built-in escape mechanisms for.
For your example:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
setenv("MY_DIR", myDir, 1);
if (system("mkdir \"${MY_DIR}\"") != 0) {
return 1;
}
// continue....