How to pass arguments to bash script from a cpp program - c++

I've forked a child process which then calls a bash script using execv, the way i'm passing command line arguments to the script, It does not print first argument on doing echo $1 inside the script.
std::string s = std::to_string(c_no);
char *args[] = {(char *)s.c_str(), NULL};
pid_t pid = fork();
if(pid == 0){
execv("./ckpnt.sh", &args[0]);
}
consider c_no to be any integer.
What is the correct way to do this?
I've already refrenced this link How to pass command line arguments from C program to the bash script? but this answer uses system system call and i try to not use that.

First argument passed to a program is its name so currently your number ends up in $0. args should be:
char *args[] = {"./ckpnt.sh", (char *)s.c_str(), NULL};

Related

Passing Arguments Through system() in C++

How would I pass a command line variable though a system() command in c++.
I have tried using:
string i;
i = system("./findName.sh");
i += argv[1];
cout << i;
But when i run this it gives me my condition for wrong number of arguments i have written in my shell script.
This is the output I received when running my program with "./findName brandonw". Which is my executable file ran with the argument i want my shell script to run with.
The arguments you put are:
brandonw
usage: findName.sh [only_one_argument]
Just concatenate it to the command string.
string command = "./findName.sh";
command = command + " " + argv[1];
system(command.c_str());
Just rearrange your code a bit:
string i("./findName.sh ");
i += argv[1];
system(i.c_str());
cout << i;
Also note, that system doesn't return a std::string, but an implementation defined int value.
If you need to deal with ./findName.sh's output, you rather need pipe().

About fork() while building a shell

here is my code.
pid_t fpid=fork();
if(fpid > 0){
wait(&fpid);
}
else{
do_command();
}
The thing is, the function do_command() only execute one line, so I change the do_command() for this:
else{
execlp("/bin/ls","ls","-al",NULL);
cout<<"\ntest<<endl;
}
Also, the ls command was executed but cout command was missing..
What's wrong with my code?
Please excuse my poor English and help me.
Here is my do_command() function declaration:
void do_command(const char *command) {
//all commands with arguments
const char *kernel_address = "/bin/";
char *kernel_command;
strcpy(kernel_command, kernel_address);
strcat(kernel_command, command);
cout << "\nCommand is:" << kernel_command << "\n" << endl;
execlp(kernel_command, command, NULL);
}
Also, there is no any output while function was called in child process
The next line is not printing because the exec family commands only returns on error. If there is no error, it will replace you program and whatever command intended to be executed in exec, is executed. Therefore cout is not working
From the manual of execlp :
The exec() family of functions replaces the current process image with a new process image.
Once your execlp is called, your program is not running anymore. It has been replaced by ls. Thus, it will never reach the cout

How do I execute a string as a shell script in C++?

I'm writing a program that needs to be able to execute a shell script provided by the user. I've gotten it to execute a single shell command, but the scripts provided will be more complicated than that.
Googling got me as far as the following code snippet:
FILE *pipe;
char str[100];
// The python line here is just an example, this is *not* about executing
// this particular line.
pipe = popen("python -c \"print 5 * 6\" 2>&1", "r");
fgets(str, 100, pipe);
cout << "Output: " << str << endl;
pclose(pipe)
So that this point str has 30 in it. So far so good. But what if the command has carriage returns in it, as a shell script file would, something like the following:
pipe = popen("python -c \"print 5 * 6\"\nbc <<< 5 + 6 2>&1", "r");
With this my goal is that str eventually have 30\n11.
To put another way, assume I have a file with the following contents:
python -c "print 5 * 6"
bc <<< 5 + 6
The argument I'm sending to popen above is the string representation of that file. I want to, from within C++, send that string (or something similar) to bash and have it execute exactly as if I were in the shell and sourced it with . file.sh, but setting the str variable to what I would see in the shell if it were executed there, in this case, 30\n11.
Yes, I could write this to a file and work it that way, but that seems like it should be unnecessary.
I wouldn't think this was a new problem, so either I'm thinking about it in a completely wrong way or there's a library that I simply don't know about that already does this.
use bash -c.
#include <stdio.h>
int main()
{
FILE *pipe = popen("bash -c \"echo asdf\necho 1234\" ", "r");
char ch;
while ((ch = fgetc(pipe)) != EOF)
putchar(ch);
}
Output:
asdf
1234
(I've test on cygwin)

how to pass command-line arguments as a string to an embedded Python script executed from C++?

I have a C++ program which exposes a Python interface to execute users' embedded Python scripts.
The user inserts the path of the Python script to run and the command-line arguments.
Then the script is executed through
boost::python::exec_file(filename, main_globals, main_globals)
To pass the command-line arguments to the Python script we have to set them through the Python C-API function
PySys_SetArgv(int args, char** argv)
before calling exec_file().
But this requires to tokenize the user's string containing the command-line arguments to get the list of arguments, and then to pass them back to the Python interpreter through PySys_SetArgv.
And that's more than a mere waste of time, because in this way the main C++ program has to take the responsibility of tokenizing the command-line string without knowing the logics behind, which is only defined in the custom user's script.
A much nicer and cleaner approach would be something like this in metacode:
string command_line_args = '-v -p "filename" -t="anotherfile" --list="["a", "b"]" --myFunnyOpt'
exec_file( filename, command_line_args, ...)
I spent hours looking at the Boost and Python C-API documentation but I did not find anything useful.
Do you know if there is a way to achieve this, i.e. passing a whole string of command line
arguments to an embedded Python script from C++?
Update:
As Steve suggested in the comments here below, I solved my problem tokenizing the input string, following https://stackoverflow.com/a/8965249/320369.
In my case I used:
// defining the separators
std::string escape_char = "\\"; // the escape character
std::string sep_char = " "; // empty space as separator
std::string quote_char = ""; // empty string --> we don't want a quote char'
boost::escaped_list_separator<char> sep( escape_char, sep_char, quote_char );
because I wanted to be able to parse tuples containing strings as well, like:
'--option-two=("A", "B")'
and if you use:
escaped_list_separator<char> sep('\\', ' ', '"');
as in the original post, you don't get the quoted strings tokenized correctly.
Since you are not adverse to executing an external file, you can use a helper program to make your shell command do the parsing for you. Your helper program could be:
#include <stdio.h>
int main (int argc, char *argv[])
{
for (int i = 1; i < argc; ++i) printf("%s\n", argv[i]);
return 0;
}
And then you could have code that sends your single string of arguments to the helper program (perhaps using popen) and read back the parsed arguments, each arg on a separate line.
unparsed_line.insert(0, "./parser_helper ");
FILE *helper = popen(unparsed_line.c_str(), "r");
std::vector<std::string> args;
std::vector<const char *> argv;
std::string arg;
while (fgetstring(arg, helper)) {
args.push_back(arg);
argv.push_back(args.rbegin()->c_str());
}
pclose(helper);
The fgetstring routine is something I wrote that is like a cross between fgets and std::getline. It reads from the FILE * one line at a time, populating a std:string argument.
static bool
fgetstring (std::string &s, FILE *in)
{
bool ok = false;
std::string r;
char buf[512];
while (fgets(buf, sizeof(buf), in) != 0) {
++ok;
r += buf;
if (*r.rbegin() == '\n') {
r.resize(r.size()-1);
break;
}
}
if (ok) s = r;
return ok;
}
I seem to remember a post on SO that had a routine similar to this, but I couldn't find it. I'll update my post if I find it later.

Arguments being passed to CreateProcess aren't being parsed as I expected

I'm trying to use devcon.exe to check the status of various pieces of hardware. In the example I'm trying to check my SATA HBA status but devcon is whining about it. Here's the code:
int main(int argc, char** argv) {
std::string cmdLine("\"C:\\Users\\afalanga\\Documents\\Visual Studio 2010\\Projects\\PlayGround\\Debug\\devcon.exe\" status PCI\\VEN_8086^&DEV_3A22^&SUBSYS_75201462^&REV_00");
char* pCmdLine(new char[cmdLine.length() + 10]);
memset(pCmdLine, 0, cmdLine.length() + 10);
for(int i(0); i < cmdLine.length(); i++)
pCmdLine[i] = cmdLine.at(i);
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = {0};
if(!CreateProcess(NULL, pCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
std::cout << "Create child process failed. Error code: "
<< GetLastError() << std::endl;
return 1;
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
}
The problem is that when the above executes, devcon complains that, "No matching devices were found." However, if I copy/paste that command line from the debugger into my command prompt and hit the enter key (or course deleting the all encompassing quotes that the debugger puts around it), the command executes perfectly as expected.
What am I getting wrong in my handing of the string? What's above is the result of reading the CreateProcess() docs on MSDN (found out that the first argument isn't necessarily needed and the cmd args shouldn't go there at all). The reason I'm allocating 10 extra bytes of memory to copy the string into is so that "whatever" may change down in the guts of the CreateProcess() function can do so without stomping on other memory. At least, that was my thought when I did that.
Command line metacharacters are parsed by the command processor. In particular you are using the ^ to prevent CMD.EXE from breaking the command at the ampersand. But you are executing the program directly, bypassing CMD.EXE. Therefore, the ^ passes through to devcon.exe who gets confused by them.
Solution: Remove the ^ characters.
Your problem is actually the opposite of your title. The command line you passed to CreateProcess is being passed directly to the application exactly as you specified it.
std::string cmdLine("\"C:\\Users\\afalanga\\Documents\\Visual Studio 2010\\Projects\\PlayGround\\Debug\\devcon.exe\" status PCI\\VEN_8086^&DEV_3A22^&SUBSYS_75201462^&REV_00
Presumably the ^ carets in there are residues from a command entered in the command line interpreter, where they serve to turn off the special meaning of &.
Simply remove the carets.
Also note that your current code leaks memory.
To avoid that, do e.g.
string commandLineArg = cmdLine + '\0';
... CreateProcess( 0, &commandLineArg[0], ... )
Can you try like this:
CreateProcess(NULL, pCmdLine.c_str(), ...);
I used:
TCHAR var[] = _T(" C:\\filepathe\\foo");
CreateProcess(NULL, var,...);