So as the title says, I would like to run a shell command on the same shell process/instance in C++ and communicate with it, how can I do that?
I have looked up every corner and I couldn't find an appropriate/straight to the point answer.
I am not a C++ overlord, my answer could be dumb.
Now I could use a combination of fork/exec on unix, but I'm on windows.
If there is a cross platform solution, please mention it below.
Thanks in advance.
Pseudocode:
SHELL shell = make_shell();
shell.run("cd desktop");
shell.run("dir");
print(shell.stdout)
The standard library in C++ does not provide a way to do what you want. However, you can use a third party library, like boost::process, which will make the communication part between your program and the sub-processes pretty portable. If you want to execute built-in shell commands, or commands using the shell, you will still need to deal with differences in the shells used on the different platforms.
The boost::process library contains a lot of different ways of creating and communicating with sub processes (reference), both synchronously and asynchronously, so browse through the reference page to get a feeling for what it can do.
Here's a simple example that uses boost::process::system to run the command dir in the directory desktop and collects the output from that command. It does not use the shell (which should generally be avoided) but uses boost::process::search_path to find the command (in your PATH) and executes the command directly without involving a shell:
#include <boost/process.hpp>
#include <boost/process/start_dir.hpp>
#include <iostream>
#include <string>
int main() {
namespace bp = ::boost::process;
bp::ipstream out;
bp::ipstream err;
bp::system(bp::search_path("dir"),
bp::start_dir = "./desktop",
bp::std_out > out, // collect stdout in out
bp::std_err > err, // collect stderr in err
bp::std_in < stdin); // for commands reading from stdin
// display what was collected on stdout and stderr:
std::string line;
std::cout << "stdout capture:\n";
while(std::getline(out, line)) {
std::cout << '[' << line << "]\n";
}
std::cout << "\nstderr capture:\n";
while(std::getline(err, line)) {
std::cout << '[' << line << "]\n";
}
}
Note: You need to link with boost_filesystem and boost_atomic for this example to work.
Linux: exec
Windows: CreateProcessA
write ifdefs to switch based upon platform.
I found this answer: https://stackoverflow.com/a/57096619/9282847.
It seems to be the only method which is both standard, and portable (assuming the implementation supports std::filesystem).
Like:
#include <filesystem>
#include <iostream>
int main() {
namespace fs = std::filesystem;
const auto kDesktopPath = fs::path("~/Desktop"); // portable filepath
fs::current_path(kDesktopPath); // set CWD (This will throw on compiler explorer)
auto dir_it = fs::recursive_directory_iterator{ fs::current_path() };
for (auto& f: dir_it) {
std::cout.put('\n') << f;
}
}
Live example on compiler explorer
Related
Using a c++ program i can successfully send commands to an arduino. The code uses the command:
system("$echo [command] > dev/ttyACM0");
Currently i must manually input the commands into this space, I was wondering if it's possible for a user to input the command, and for it to then be added to the string within system()?
This is an approximation of what I think you want:
#include <fstream>
#include <iostream>
#include <string>
int main() {
std::string command;
if(std::getline(std::cin, command)) { // read user input
std::ofstream ard("/dev/ttyACM0"); // open the device
if(ard) {
ard << command << '\n'; // send the command
}
} // here `ard` goes out of scope and is closed automatically
}
Note that you do not need the unsafe system() command at all here. Just open the device and send the string directly.
Recently I want to enhance CMD in Win10 by myself using C++. I don't want to change the original framework of it but to translate the command. At first i wrote something like this:
#include <unistd.h>
#include <iostream>
#include <string>
using namespace std;
int main()
{
string initial = "retr0# ";
string s;
while(1)
{
cout << initial;
getline(cin,s);
if(s!="exit")
{
system(s.c_str());
cout << "------" << endl;
}
else break;
}
system("pause");
return 0;
}
But I found that if you entered command like "E:" or something else to change the directory, it is impossible for the new thread to inherit the context. My question is, how to solve the problem like this?
In most operating systems (including 1970 era Unix), the working directory is specific to each process.
The system function will run another process. So even if you change its working directory, it only affects the process started by system, not the process running your program.
So you need to define a syntax (perhaps the same cd as Windows CMD has) and parse and implement that command in your own program. You could use SetCurrentDirectory or _chdir
I'm writing a small CLI application and I want to allow the user to redirect to a file while standard cout statements go to the output.txt file I want progress to always to go the screen.
./myApp > output.txt
10% complete
...
90% complete
Completed
Is this possible? How can I do it?
Thanks in advance!!
This will work even if both stdin and stdout have been redirected:
spectras#etherbee:~$ ./term
hello terminal!
spectras#etherbee:~$ ./term >/dev/null 2>&1
hello terminal!
The idea is to open the controlling terminal of the process directly, bypassing any redirection, like this:
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("/dev/tty", O_WRONLY);
if (fd < 0 && errno != ENODEV) {
/* something went wrong */
return 1;
}
int hasTTY = (fd >= 0);
if (hasTTY) {
write(fd, "hello terminal!\n", 16);
}
return 0;
}
From man 4 tty:
The file /dev/tty is a character file with major number 5 and
minor number 0, usually of mode 0666 and owner.group root.tty. It is
a synonym for the controlling terminal of a process, if any.
If you're using C++, you might want to wrap the file descriptor into a custom streambuf, so you can use regular stream API on it. Alternately, some implementations of the C++ library offer extensions for that purpose. See here.
Or, if you don't care about getting the error code reliably, you could just std::ofstream terminal("/dev/tty").
Also as a design consideration if you do this, offering a quiet option to let the user turn off the writing to the terminal is a good idea.
Your process cannot know if the shell redirects the standard console output (std::cout) or not.
So you'll need another handle that lets you output to the terminal independently of that redirection.
As #Mark mentioned in their comment you could (ab-)use1 std::cerr to do that, along with some ASCII trickery to overwrite the current output line at the terminal (look at backspace characters: '\b').
1)Not to mention the mess printed at the terminal if the output isn't actually redirected.
You can write your progress indicators to the stderr stream. They will appear on the console if the user redirects stdout to a file.
For example:
fprintf(stderr, "10%% complete\n");
I figured out how to do it, even if the user redirects stderr. The following code gets the name of the current terminal and checks to see if our output is being redirected. It also has a my_write() function that allows you to write to both the terminal and the redirect file, if they've redirected stdout. You can use the my_write() function with the writetoterm variable where-ever you want to write something that you want to always be written to the terminal. The extern "C" has to be there, otherwise (on Debian 9 with GCC 6.3, anyway) the ttyname() function will just return NULL all the time.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <error.h>
#include <errno.h>
#include <sstream>
using std::string;
using std::fstream;
using std::cout;
using std::endl;
using std::cerr;
using std::stringstream;
void my_write(bool writetoterm, int termfd, string data)
{
if(writetoterm)
{
int result = write(termfd, data.c_str(), data.length());
if(result < data.length()){
cerr << "Error writing data to tty" << endl;
}
}
cout << data;
}
extern "C" {
char* GetTTY(int fd){
//printf("%s", ttyname(fd));
return ttyname(fd);
}
}
int main(int argc, char** argv){
getenv("TTY");
bool writetoterm = false;
struct stat sb = {};
if(!GetTTY(STDOUT_FILENO)){
//not a TTY
writetoterm = true;
}
int ttyfd = open(GetTTY(2), O_WRONLY);
if(ttyfd < 0){
//error in opening
cout << strerror(errno) << endl;
}
string data = "Hello, world!\n";
my_write(true, ttyfd, data);
int num_for_cout = 42;
stringstream ss;
ss << "If you need to use cout to send something that's not a string" << endl;
ss << "Do this: " << num_for_cout << endl;
my_write(writetoterm, ttyfd, ss.str().c_str());
return 0;
}
I found the official std:: method of handling this. There is another type... std::clog. This is specifically for information and always appears on the command line even though the user redirects the output of the program myProgram > out.txt.
Thanks this was great to see all the methods that this can be done.
I'm trying to make a program that a bash script runs. I want the bash script to be able to change the state of the c++ program, and the only thing I could find was to use environment variables. Thing is, its seems getenv only gets the value at the time when the program was run.
Bash
export BLINK=1
./blink &
sleep 5s
unset BLINK
C++
int main(int args, char **argv) {
char *blink = getenv("BLINK");
while(blink && blink[0] == '1')
{
std::cout << getenv("BLINK") << std::endl;
usleep(500000);
}
return 1;
}
So what this does is run the blink program, wait 5 seconds then unset the environment. The C++ program however always sees the enviorment value as 1 and never stops. How do I get an updated environment variable while the program is running? Or is there a better way to have a bash script control the state of a c++ program.
EDIT I should note, I do not want to just kill the process either because it has to turn off hardware when it ends.
It is not possible to modify program environment after it is started. You have to use another method of interprocess communication. The simplest one is to register handler for some signal to your app (e.g. SIGUSR1), and then send it using kill -SIGUSR1 <pid> command.
There are also other solutions available, e.g. create named pipe (using pipe shell command), and check periodically if someone wrote something to it. If yes, exit loop.
You can also use sockets if you want, but this could be more complicated.
The C++ programs environment is not changing when you change it in bash - since when you start your program it gets a copy of the parent's environment and has no access to the parent process's environment.
You can use a file to easily share data, however.
Bash
echo "1" > blink.txt
./blink &
sleep 5s
echo "0" > blink.txt
C++
#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>
#include <unistd.h>
using namespace std;
string getfile(const string& filename) {
ifstream in(filename.c_str(), ios_base::binary);
in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit);
return string(istreambuf_iterator<char>(in), istreambuf_iterator<char>());
}
int main(int argc, char* argv[]) {
string blink = getfile("blink.txt");
while(blink[0] == '1')
{
std::cout << getfile("blink.txt") << std::endl;
usleep(500000);
}
return 1;
}
Say I have an .exe, lets say sum.exe. Now say the code for sum.exe is
void main ()
{
int a,b;
scanf ("%d%d", &a, &b);
printf ("%d", a+b);
}
I wanted to know how I could run this program from another c/c++ program and pass input via stdin like they do in online compiler sites like ideone where I type the code in and provide the stdin data in a textbox and that data is accepted by the program using scanf or cin. Also, I wanted to know if there was any way to read the output of this program from the original program that started it.
The easiest way I know for doing this is by using the popen() function. It works in Windows and UNIX. On the other way, popen() only allows unidirectional communication.
For example, to pass information to sum.exe (although you won't be able to read back the result), you can do this:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
f = popen ("sum.exe", "w");
if (!f)
{
perror ("popen");
exit(1);
}
printf ("Sending 3 and 4 to sum.exe...\n");
fprintf (f, "%d\n%d\n", 3, 4);
pclose (f);
return 0;
}
In C on platforms whose name end with X (i.e. not Windows), the key components are:
pipe - Returns a pair of file descriptors, so that what's written to one can be read from the other.
fork - Forks the process to two, both keep running the same code.
dup2 - Renumbers file descriptors. With this, you can take one end of a pipe and turn it into stdin or stdout.
exec - Stop running the current program, start running another, in the same process.
Combine them all, and you can get what you asked for.
This is my solution and it worked:
sum.cpp
#include "stdio.h"
int main (){
int a,b;
scanf ("%d%d", &a, &b);
printf ("%d", a+b);
return 0;
}
test.cpp
#include <stdio.h>
#include <stdlib.h>
int main(){
system("./sum.exe < data.txt");
return 0;
}
data.txt
3 4
Try this solution :)
How to do so is platform dependent.
Under windows, Use CreatePipe and CreateProcess. You can find example from MSDN :
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
Under Linux/Unix, you can use dup() / dup2()
One simple way to do so is to use a Terminal (like command prompt in windows) and use | to redirect input/output.
Example:
program1 | program2
This will redirect program1's output to program2's input.
To retrieve/input date, you can use temporary files, If you don't want to use temporary files, you will have to use pipe.
For Windows, (use command prompt):
program1 <input >output
For Linux, you can use tee utility, you can find detail instruction by typing man tee in linux terminal
It sounds like you're coming from a Windows environment, so this might not be the answer you are looking for, but from the command line you can use the pipe redirection operator '|' to redirect the stdout of one program to the stdin of another. http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true
You're probably better off working in a bash shell, which you can get on Windows with cygwin http://cygwin.com/
Also, your example looks like a mix of C++ and C, and the declaration of main isn't exactly an accepted standard for either.
How to do this (you have to check for errors ie. pipe()==-1, dup()!=0, etc, I'm not doing this in the following snippet).
This code runs your program "sum", writes "2 3" to it, and than reads sum's output. Next, it writes the output on the stdout.
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
int main() {
int parent_to_child[2], child_to_parent[2];
pipe(parent_to_child);
pipe(child_to_parent);
char name[] = "sum";
char *args[] = {name, NULL};
switch (fork()) {
case 0:
// replace stdin with reading from parent
close(fileno(stdin));
dup(parent_to_child[0]);
close(parent_to_child[0]);
// replace stdout with writing to parent
close(fileno(stdout));
dup(child_to_parent[1]);
close(child_to_parent[1]);
close(parent_to_child[1]); // dont write on this pipe
close(child_to_parent[0]); // dont read from this pipe
execvp("./sum", args);
break;
default:
char msg[] = "2 3\n";
close(parent_to_child[0]); // dont read from this pipe
close(child_to_parent[1]); // dont write on this pipe
write(parent_to_child[1], msg, sizeof(msg));
close(parent_to_child[1]);
char res[64];
wait(0);
read(child_to_parent[0], res, 64);
printf("%s", res);
exit(0);
}
}
I'm doing what #ugoren suggested in their answer:
Create two pipes for communication between processes
Fork
Replace stdin, and stdout with pipes' ends using dup
Send the data through the pipe
Based on a few answers posted above and various tutorials/manuals, I just did this in Linux using pipe() and shell redirection. The strategy is to first create a pipe, call another program and redirect the output of the callee from stdout to one end of the pipe, and then read the other end of the pipe. As long as the callee writes to stdout there's no need to modify it.
In my application, I needed to read a math expression input from the user, call a standalone calculator and retrieve its answer. Here's my simplified solution to demonstrate the redirection:
#include <string>
#include <unistd.h>
#include <sstream>
#include <iostream>
// this function is used to wait on the pipe input and clear input buffer after each read
std::string pipeRead(int fd) {
char data[100];
ssize_t size = 0;
while (size == 0) {
size = read(fd, data, 100);
}
std::string ret = data;
return ret;
}
int main() {
// create pipe
int calculatorPipe[2];
if(pipe(calculatorPipe) < 0) {
exit(1);
}
std::string answer = "";
std::stringstream call;
// redirect calculator's output from stdout to one end of the pipe and execute
// e.g. ./myCalculator 1+1 >&8
call << "./myCalculator 1+1 >&" << calculatorPipe[1];
system(call.str().c_str());
// now read the other end of the pipe
answer = pipeRead(calculatorPipe[0]);
std::cout << "pipe data " << answer << "\n";
return 0;
}
Obviously there are other solutions out there but this is what I can think of without modifying the callee program. Things might be different in Windows though.
Some useful links:
https://www.geeksforgeeks.org/pipe-system-call/
https://www.gnu.org/software/bash/manual/html_node/Redirections.html