How do I execute a string as a shell script in C++? - 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)

Related

How do I read input .in files in command line on windows

I completed my code but the problem is I don't know how to test it based on instructions given.
The instructor provided us with 3 input files with the following values:
file1: 33 20
file2: 5 7
file3: 18 15
I'm supposed to take these values and create event objects with these stored values. Problem is the instructor is giving the testing method on Ubuntu and she displayed how to input file in command line like:
./sApp < simulationShuffled3.in
So i'm just really confused as to how I'm suppose to get it working. I am currently using Windows console, VStudios and sublime text with a terminal attachment.
The code I'm currently using that's following from an example from my course notes is
while (getline(cin >> ws, aLine)) { // while (there is data)
stringstream ss(aLine);
ss >> arrivalTime >> processingTime;
Event newEvent = Event('A',arrivalTime,processingTime);
eventPriorityQueue.enqueue(newEvent);
}
Read input from stdin in Windows [&/or] Linux
You can do this in three ways:
In Command prompt (windows)
In Powershell (windows/linux)
In WSL & Linux
1: in Windows Cmd
type input.in | python file_requiring_input.py # in python
type input.in | go run file_requiring_input.go # in go
type input.in | node file_requiring_input.js # in js
javac file_requiring_input.java &&
type input.in | java file_requiring_input # in java
g++ file_requiring_input.cpp &&
type input.in | a.exe # in cpp
gcc file_requiring_input.c &&
type input.in | a.exe # in c
Another way
python file_requiring_input.py < input.in # in python
g++ file_requiring_input.cpp &&
a.exe < input.in # in cpp
2: in Powershell
gc .\input.in | python file_requiring_input.py # in python
g++ .\file_requiring_input.cpp ;
gc .\input.in | .\a.exe # in cpp
gc is short for Get-Content. Other aliases of gc are cat and type.
which means that all three names can be used as replacement or gc
3: use wsl for windows //Linux commands
cat input.in | python file_requiring_input.py # in python
g++ file_requiring_input.cpp &&
cat input.in | ./a.out # in cpp
another way
python file_requiring_input.py < input.in # in python
g++ file_requiring_input.cpp &&
./a.out < input.in # in cpp
have edited the code testing everything out hopefully. Do correct me if I am wrong or if anything more is to be added .
Also input.in is more generally a text file so you can create input.txt file as you wish and it would work the same
Well, as this is an assignment, I'm not going to provide you with a ready-made code. Rather, I'll just point you to the right direction.
In Windows, you can provide the arguments to your executable separated by space on Command Prompt like this:
C:\Assignment> Test.exe file1.in file2.in file3.in
And, it'll work on Ubuntu as well.
So, you need to study Command Line Arguments, File Handling, reading from file; and, you'll have to convert these strings read from files to integers.
Command Line Arguments: http://en.cppreference.com/w/c/language/main_function
File Handling: http://en.cppreference.com/w/cpp/io/basic_fstream
Here's a minimal example for reading from a file (std::ifstream):
I've a test file at C:\Test\Test.txt with the following contents:
11 22
12 23
23 34
Here's is main.cpp to test it:
#include <iostream>
#include <fstream>
#include <string>
int main( int argc, char *argv[] )
{
const std::string filename { R"(C:\Test\Test.txt)" };
std::ifstream ifs { filename };
if ( !ifs.is_open() )
{
std::cerr << "Could not open file!" << std::endl;
return -1;
}
int arrivalTime { 0 };
int processingTime { 0 };
while ( ifs >> arrivalTime >> processingTime )
{
std::cout << "Arrival Time : " << arrivalTime << '\n'
<< "Processing Time : " << processingTime << std::endl;
}
ifs.close();
return 0;
}
Output:
Arrival Time : 11
Processing Time : 22
Arrival Time : 12
Processing Time : 23
Arrival Time : 23
Processing Time : 34

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().

Extracting .rar/.zip using c++

For no particular reason, I'm currently working on a program that extracts .zip/.rar files using system().
I currently have WinRar installed because winrar.exe is able to handle both .zip & .rar files.
int main()
{
vector<wstring> files;
if (ListFiles(L"folder", L"*", files))
{
string program = "\"C:\\Program Files\\WinRAR\\winrar.exe\"";
string args = "x -y";
string type = "*.*";
TCHAR dir[MAX_PATH];
GetCurrentDirectory(MAX_PATH, dir);
wstring current_directory(wstring(L"\"") + dir + wstring(L"\\"));
for (const auto& f : files)
{
if (wcscmp(PathFindExtension(f.c_str()), L".rar") == 0 ||
wcscmp(PathFindExtension(f.c_str()), L".zip") == 0)
{
string file = ws2s(f.c_str());
string output = "\"c:\\Users\\my name\\Desktop\\output\"";
string command = program + " " + args + " " + ws2s(current_directory) + file + "\"" + " " + type + " " + output;
cout << command << endl;
if (system(command.c_str()) != 0)
return GetLastError();
}
}
}
return 0;
}
Because I'm using the command line, and don't want spaces to be a problem I wrap what I can in quotation marks:
-- "C:/users/username/program files (x86)/" --
-- "folder/zipped folder.zip" vs folder/"zipped folder.zip" --
After building the complete command contained in command, I printed it out to the screen so I could Edit->Mark:
"C:\Program Files\WinRAR\winrar.exe" x -y "C:\Users\my name\Documents\Visual Studio 2013\Projects\extractor\folder\unzip.zip" *.* "c:\Users\my name\Desktop\output"
However, 'C:\Program' is not recognized as an internal or external command,
operable program or batch file. is what I'm met with after the system(command) call.
If I Copy & Paste the exact same command into Start->Command Prompt, it works like a dream.
How to extract ZIP files with WinRAR command line?
http://comptb.cects.com/using-the-winrar-command-line-tools-in-windows/
https://www.feralhosting.com/faq/view?question=36
Is there a way different way to invoke the system() call?
If there's not, how else can command line arguments be used?
I'd prefer to [avoid entirely] not use Boost:: or 3rd party libraries.
Thanks!
This is probably because of the quirky behavior of Command Prompt when it comes to quotation of arguments. Whenever you call system("\"arg1\" \"arg2\""), it is equivalent to calling:
cmd.exe /c "arg1" "arg2"
Because of the strange behavior as described in the linked post, this will not be interpreted correctly by Command Prompt. An extra set of quotes is needed:
cmd.exe /c ""arg1" "arg2""
For invoking executables, CreateProcess provides an alternative that gives you more control over the process. You'll still have to quote the arguments but the rules are a bit simpler as the Command Prompt is no longer in your way.

devc++ input from file does not work

I'm trying to redirect a .txt content to .exe
program.exe < file.txt
and contents of file.txt are
35345345345
34543534562
23435635432
35683045342
69849593458
95238942394
28934928341
but the first index in array is the file path and the file contents is not displayed.
int main(int argc, char *args[])
{
for(int c = 0; c<argc; c++){
cout << "Param " << c << ": " << args[c] << "\n";
}
system("PAUSE");
return EXIT_SUCCESS;
}
Desired output:
Param0: 35345345345
Param1: 34543534562
Param2: 23435635432
Param3: 35683045342
Param4: 69849593458
Param5: 95238942394
Param6: 28934928341
The myapp < file.txt syntax passes to stdin (or cin if you prefer), not the arguments.
You have misunderstood what argc and argv are for. They contain the command line arguments to your program. If, for example, you ran:
program.exe something 123
The null terminated strings pointed to by argv will be program.exe, something, and 123.
You are attempting to redirect the contents of a file to program.exe using < file.txt. This is not a command line argument. It simply redirects the contents of the file to the standard input of your program. To get those contents you will need to extract from std::cin.
When you say "but the first index in array is the file path and the file contents is not displayed." it sounds like you're trying to read input from argv and argc. The angle bracket shell operator does not work that way. Instead, stdin (what cin and several C functions read from) has the contents of that file. So, to read from the file in the case above, you'd use cin.
If you instead really wanted to have a file automatically inserted into the argument list, I can't help you with the windows shell. However, if you have the option of using bash, the following will work:
program.exe `cat file.txt`
The backtick operator expands into the result of the command contained within, and so the contents are then passed as arguments to program.exe (again, under the bash shell and not the windows shell)
This code does what i was expecting to do with the other one. Thanks everybody who helped.
#include <iostream>
#include <string>
using namespace std;
int main()
{
string line;
while (getline(cin, line))
cout << "line: " << line << '\n';
}

problem in reading output of dd command using pipe

I am developing an application, in which I want to redirect the output (progress information) of dd command to my C++ program, but it is not actually getting the output, here is the code
FILE * progressInfo = popen("gzip -dc backup/backup.img.gz | pv -ptrbe -i 2 -s 2339876653 | dd of=/dev/sdb","r");
if(!progressInfo)
{
return -1;
}
char buf[1024];
while(fgets(buff, sizeof(buff),progressInfo)!=NULL)
{
std::cout << buff << endl;
}
but the problem is the progress information is not received in buff, and the output is continuously printed on terminal, and above program halts on while(fgets(buff, sizeof(buff),progressInfo)!=NULL), and as soon as the dd operation is completed, the very next line to loop block is executed.
if anyone has any idea why the output is not returned to buff, and its continuously retuned on terminal?
The output is probably being written to standard error rather than standard output. Just add " 2>&1" to the very end of your command string and you should see the output (note the leading space).