According to the question " How to get Linux distribution name and version? ", to get the linux distro name and version, this works:
lsb_release -a
On my system, it shows the needed output:
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 9.10
Release: 9.10
Codename: karmic
Now, to get this info in C++, Qt4's QProcess would be a great option but since I am developing without Qt using std c++, I need to know how to get this info in standard C++, i.e. the stdout of the process, and also a way to parse the info.
Uptil now I am trying to use code from here but am stuck on function read().
You can simply use the function:
int uname(struct utsname *buf);
by including the header
#include <sys/utsname.h>
It already returns the name & version as a part of the structure:
struct utsname
{
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined network" */
char release[]; /* OS release (e.g., "2.6.28") */
char version[]; /* OS version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};
Am I missing something?
For recent linux distros you can use following to get the OS info. The output is pretty standard and can be parsed using following spec:
https://www.freedesktop.org/software/systemd/man/os-release.html
cat /etc/os-release
Sample outputs:
NAME=Fedora
VERSION="27 (Twenty Seven)"
ID=fedora
VERSION_ID=27
PRETTY_NAME="Fedora 27 (Twenty Seven)"
NAME="Ubuntu"
VERSION="16.04.4 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.4 LTS"
VERSION_ID="16.04"
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
ID_LIKE=archlinux
ANSI_COLOR="0;36"
Got it from cplusplus.com forums, a simple call GetSystemOutput("/usr/bin/lsb_release -a") works.
char* GetSystemOutput(char* cmd){
int buff_size = 32;
char* buff = new char[buff_size];
char* ret = NULL;
string str = "";
int fd[2];
int old_fd[3];
pipe(fd);
old_fd[0] = dup(STDIN_FILENO);
old_fd[1] = dup(STDOUT_FILENO);
old_fd[2] = dup(STDERR_FILENO);
int pid = fork();
switch(pid){
case 0:
close(fd[0]);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
system(cmd);
//execlp((const char*)cmd, cmd,0);
close (fd[1]);
exit(0);
break;
case -1:
cerr << "GetSystemOutput/fork() error\n" << endl;
exit(1);
default:
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
int rc = 1;
while (rc > 0){
rc = read(fd[0], buff, buff_size);
str.append(buff, rc);
//memset(buff, 0, buff_size);
}
ret = new char [strlen((char*)str.c_str())];
strcpy(ret, (char*)str.c_str());
waitpid(pid, NULL, 0);
close(fd[0]);
}
dup2(STDIN_FILENO, old_fd[0]);
dup2(STDOUT_FILENO, old_fd[1]);
dup2(STDERR_FILENO, old_fd[2]);
return ret;
}
int writepipe[2];
if (pipe(writepipe) < 0) {
perror("pipe");
return 1;
}
int ret = fork();
if (ret < 0) {
perror("fork");
return 1;
}
else if (ret == 0) // child process
{
dup2(writepipe[1],1); // set writepipe[1] as stdout
// close fds
close(writepipe[0]);
close(writepipe[1]);
execlp("lsb_release","lsb_release","-a",NULL); //TODO: Error checking
}
else // parent process
{
int status;
waitpid(ret,&status,0); //TODO: Error checking
//do what you need
//read output of lsb_release from writepipe[0]
}
It works for me
There are files named /etc/version and /etc/release which have information like whether you're using Ubuntu or Fedora, etc. (which is what the OP clarified his question to be).
Personally I like the uname solution posted by #Alok Slav, but in case it helps someone who needs to use a command-line utility to get the info, consider using popen.
Related
While I was playing with pipes in c++ I stumbled accross something rather interesting.
#include <cstdio>
#include <iostream>
#include <string>
int main()
{
FILE *pystream = popen("python","w"); // Calling the python console
fprintf(pystream,"print(2+3)"); // Making it do something
pclose(pystream); // Closing the pipe
return 0;
}
This code outputs 5. but why ? And can the "output" be read or stored somewhere ?
I'm fairly new to C buffers and pipes, so I don't know if I'm using the right terminology.
When you write like this you're effectively writing to the stdin of the process you just started, in this case the python REPL. On Linux the python REPL is getting the expression directly ie it's not being typed in. This is ths system command
read(0, "print(2+3)", 4096) = 10
If you were doing this in the terminal each character is being read in one at a time by the terminal and when it gets carriage return it writes a newline \n ie
read(0, "\r", 1) = 1
write(1, "\n", 1
It then performs the calculation and write the result out
write(1, "5\n", 25
You're by passing the terminal and writing the data directly to the stdin of the python interpreter. If you want to see how this can easily break try this code.
#include <cstdio>
#include <iostream>
#include <string>
int main()
{
FILE *pystream = popen("python","w"); // Calling the python console
fprintf(pystream,"print(2+3)"); // Making it do something
fprintf(pystream,"print(2+3)"); // Making it do something
pclose(pystream); // Closing the pipe
return 0;
}
You will get a syntax error, to make it work the stdin needs to be fed a carriage return or a newline to separate the two lines ie add a carriage return...
fprintf(pystream,"print(2+3)\r");
The standard output of the command you're executing is connected to the standard output of your program, so when the Python writes to its standard output, it appears on the standard output of your process too.
If you had pending output before you ran Python, that won't be flushed and will appear after Python returns. For example,
std::cout << "Hello";
(no endl, no \n in the string) before popen() and
std::cout << " World\n";
after pclose() means that you'll see the Python output before Hello World.
If you want to write to Python and read the results back in your program, you can no longer use popen() and pclose(). Instead, you need to use pipe() twice (one pipe to talk to Python, one pipe to read from Python), and you need to use fork(), exec(), dup2() — probably; dup() otherwise — and close() to make the operations work. You'll be using file descriptors and hence read() and write() system calls in the parent process, too.
Those are all C functions (system calls) more than C++ functions.
This code works:
#include <unistd.h>
#include <cstdio>
#include <cstring>
int main()
{
int p1[2];
int p2[2];
if (pipe(p1) != 0 || pipe(p2) != 0)
return 1;
int pid;
if ((pid = fork()) < 0)
return 1;
if (pid == 0)
{
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execlp("python", "python", (char *)0);
fprintf(stderr, "failed to exec python\n");
return 1;
}
else
{
close(p1[0]);
close(p2[1]);
const char command[] = "print(2+3)\n";
int len = strlen(command);
if (write(p1[1], command, len) != len)
{
fprintf(stderr, "failed to write command to python\n");
return 1;
}
close(p1[1]);
char buffer[256];
int nbytes;
if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
{
fprintf(stderr, "failed to read response from python\n");
return 1;
}
printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
close(p2[0]);
printf("Finished\n");
}
return 0;
}
The bad news is that changing this code to write more than one command while synchronously reading a response from Python does not work. Python does not process each line separately as it does when its input is a terminal; it reads all the data before it responds at all. You can work around that with python -i, but then the prompts from Python appear on stderr. So, you can redirect that to /dev/null to lose it:
#include <unistd.h>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
int main()
{
int p1[2];
int p2[2];
if (pipe(p1) != 0 || pipe(p2) != 0)
return 1;
int pid;
if ((pid = fork()) < 0)
return 1;
if (pid == 0)
{
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
int dn = open("/dev/null", O_WRONLY);
if (dn >= 0)
{
dup2(dn, STDERR_FILENO);
close(dn);
}
execlp("python", "python", "-i", (char *)0);
fprintf(stderr, "failed to exec python\n");
return 1;
}
else
{
close(p1[0]);
close(p2[1]);
const char *commands[] =
{
"print(2+3)\n",
"print(3+4)\n",
};
enum { NUM_COMMANDS = sizeof(commands) / sizeof(commands[0]) };
for (int i = 0; i < NUM_COMMANDS; i++)
{
int len = strlen(commands[i]);
if (write(p1[1], commands[i], len) != len)
{
fprintf(stderr, "failed to write command to python\n");
return 1;
}
char buffer[256];
int nbytes;
if ((nbytes = read(p2[0], buffer, sizeof(buffer))) <= 0)
{
fprintf(stderr, "failed to read response from python\n");
return 1;
}
printf("Python said: (%d) [%.*s]\n", nbytes, nbytes, buffer);
}
close(p1[1]);
close(p2[0]);
printf("Finished\n");
}
return 0;
}
Without redirection of stderr:
Python 2.7.10 (default, Oct 23 2015, 19:19:21)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> >>> Python said: (2) [5
]
>>> Python said: (2) [7
]
Finished
With redirection of stderr:
Python said: (2) [5
]
Python said: (2) [7
]
Finished
The disadvantage of losing the standard error output to /dev/null is that you won't get any notice when Python objects to what you send it to execute — the code will hang. Working around that is fun (a third pipe, and using poll() or epoll() or — perish the thought — select() would be one way around the problem).
I'm totally newbie in Unix environment and i faced some problems with plain example from Unix Systems Programming book by Robbins.
It's plain chain of processes and each process prints some info to log file and stderr
#define BUFSIZE 1024
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_PERMS (S_IRUSR | S_IWUSR| S_IRGRP | S_IROTH)
int main (int argc, char *argv[]) {
char buf[BUFSIZE];
pid_t childpid = 0;
int i, n;
if (argc != 3){ /* check for valid number of command-line arguments */
fprintf (stderr, "Usage: %s processes filename\n", argv[0]);
return 1;
}
/* open the log file before the fork */
n = atoi(argv[1]); /* create a process chain */
for (i = 1; i < n; i++)
if (childpid = fork())
break;
if (childpid == -1) {
fprintf(stderr, "Failed to fork");
return 1;
}
auto fd = open(argv[2], CREATE_FLAGS, CREATE_PERMS);
if (fd < 0) {
fprintf(stderr,"Failed to open file");
return 1;
}
sprintf(buf, "i:%d process:%ld parent:%ld child:%ld\n",
i, (long)getpid(), (long)getppid(), (long)childpid);
fprintf(stderr, buf);
write(fd, buf, strlen(buf));
return 0;
}
It's compiled on Netbeans 7.1 with g++ 4.7 and run command is "${OUTPUT_PATH}" 10 /home/maxim/testlog.log
So the problems are:
When i run or debug project it prints out only 2 or 3 lines of info in both console and file. But if i traverse with "Step Over" through childpid = fork(), it prints info about all 10 processes. Is that some compiler optimization or just my fault?
Even when it prints all lines, the output looks like
i:2 process:6571 parent:6566 child:6572
i:3 process:6572 parent:1 child:6573
i:4 process:6573 parent:6572 child:6574
...
i:9 process:6578 parent:1 child:6579
i:10 process:6579 parent:6578 child:0
Parent pid values for some processes are 1, which seems to be wrong
If the processes each open the same output file there will be a race condition causing the processes to overwrite each other. That is why it only happens when you run at full speed.
When the parent process ends any children that are still alive are either killed or get a new parent depending on a setting in Linux. In your case they seem to get a new parent. That new parent is process 1.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I want to write a program which is capable of printing out system properties. Is this possible?
If so, with C/C++, how can one learn system features/properties?
For example, the following properties:
hardware :
Memory 3.8 gib
ubuntu :
release 10.10 ( maveric )
system status:
available disk space 51.1 gib
user name :
xxx
processor :
intel ... duo cpu e4600
The platform being Linux.
In Linux You can simple use the function:
int uname(struct utsname *buf);
by including the header
#include <sys/utsname.h>
it returns the system information as a part of the structure:
struct utsname
{
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined network" */
char release[]; /* OS release (e.g., "2.6.28") */
char version[]; /* OS version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};
Well, if not all, atleast it gives you some system properties as you said. There should be other api available which can reveal all the information you need. You will need to check out the documentation & search around a bit for that.
EDIT:
Oh well, I just ripped this one off from the internet. This program shall help you run Linux commands programatically.
char* GetSystemOutput(char* cmd)
{
int buff_size = 32;
char* buff = new char[buff_size];
char* ret = NULL;
string str = "";
int fd[2];
int old_fd[3];
pipe(fd);
old_fd[0] = dup(STDIN_FILENO);
old_fd[1] = dup(STDOUT_FILENO);
old_fd[2] = dup(STDERR_FILENO);
int pid = fork();
switch(pid)
{
case 0:
close(fd[0]);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
system(cmd);
//execlp((const char*)cmd, cmd,0);
close (fd[1]);
exit(0);
break;
case -1:
cerr << "GetSystemOutput/fork() error\n" << endl;
exit(1);
default:
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
int rc = 1;
while (rc > 0)
{
rc = read(fd[0], buff, buff_size);
str.append(buff, rc);
//memset(buff, 0, buff_size);
}
ret = new char [strlen((char*)str.c_str())];
strcpy(ret, (char*)str.c_str());
waitpid(pid, NULL, 0);
close(fd[0]);
}
dup2(STDIN_FILENO, old_fd[0]);
dup2(STDOUT_FILENO, old_fd[1]);
dup2(STDERR_FILENO, old_fd[2]);
return ret;
}
Api Usage: GetSystemOutput("/usr/bin/lsb_release -a")
And following the commands:
cat /proc/cpuinfo = tells you cpu info
cat /proc/meminfo = tells you memory info
lspci = tells you hardware that is attached (at least if the kernel recognizes it)
cat /proc/ide/hda/* = tells you info of your first ide hard-drive.
Look around in the /proc directory. There's a lot of things that might be considered system properties, but you'll soon be able to determine which properties are of interest to you.
cat /proc/somedir/somefile
is the command you want to use to safely browse /proc.
Every OS has an API that allows you to communicate with it.
Sadly it's not something uniform, you'll need to read search for your target OS's API.
Through the API you can usually get most of the information you need.
When I execute "python" from the terminal with no arguments it brings up the Python interactive shell.
When I execute "cat | python" from the terminal it doesn't launch the interactive mode. Somehow, without getting any input, it has detected that it is connected to a pipe.
How would I do a similar detection in C or C++ or Qt?
Use isatty:
#include <stdio.h>
#include <io.h>
...
if (isatty(fileno(stdin)))
printf( "stdin is a terminal\n" );
else
printf( "stdin is a file or a pipe\n");
(On windows they're prefixed with underscores: _isatty, _fileno)
Summary
For many use cases the POSIX function isatty() is all what it is needed to detect if stdin is connected to a terminal. A minimal example:
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (isatty(fileno(stdin)))
puts("stdin is connected to a terminal");
else
puts("stdin is NOT connected to a terminal");
return 0;
}
The following section compares different methods that can be used if different degrees of interactivity have to be tested.
Methods in Detail
There are several methods to detect if a program is running interactively.
Following table shows an overview:
cmd\method ctermid open isatty fstat
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
./test /dev/tty OK YES S_ISCHR
./test < test.cc /dev/tty OK NO S_ISREG
cat test.cc | ./test /dev/tty OK NO S_ISFIFO
echo ./test | at now /dev/tty FAIL NO S_ISREG
The results are from a Ubuntu Linux 11.04 system using the following program:
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
int main() {
char tty[L_ctermid+1];
ctermid(tty);
printf("ID: %s\n", tty);
int fd = open(tty, O_RDONLY);
if (fd < 0) perror("Could not open terminal");
else {
printf("Opened terminal\n");
struct termios term;
int r = tcgetattr(fd, &term);
if (r < 0) perror("Could not get attributes");
else printf("Got attributes\n");
}
if (isatty(fileno(stdin))) printf("Is a terminal\n");
else printf("Is not a terminal\n");
struct stat stats;
int r = fstat(fileno(stdin), &stats);
if (r < 0) perror("fstat failed");
else {
if (S_ISCHR(stats.st_mode)) printf("S_ISCHR\n");
else if (S_ISFIFO(stats.st_mode)) printf("S_ISFIFO\n");
else if (S_ISREG(stats.st_mode)) printf("S_ISREG\n");
else printf("unknown stat mode\n");
}
return 0;
}
Terminal device
If the interactive session needs certain capabilities, you can open the
terminal device and (temporarily) set terminal attributes you need
via tcsetattr().
Python Example
The Python code that decides whether the interpreter runs interactively uses isatty(). The Function PyRun_AnyFileExFlags()
/* Parse input from a file and execute it */
int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
PyCompilerFlags *flags)
{
if (filename == NULL)
filename = "???";
if (Py_FdIsInteractive(fp, filename)) {
int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
calls Py_FdIsInteractive()
/*
* The file descriptor fd is considered ``interactive'' if either
* a) isatty(fd) is TRUE, or
* b) the -i flag was given, and the filename associated with
* the descriptor is NULL or "<stdin>" or "???".
*/
int
Py_FdIsInteractive(FILE *fp, const char *filename)
{
if (isatty((int)fileno(fp)))
return 1;
which calls isatty().
Conclusion
There are different degrees of interactivity. For checking if stdin is connected to a pipe/file or a real terminal isatty() is a natural method to do that.
Probably they are checking the type of file that "stdin" is with fstat, something like this:
struct stat stats;
fstat(0, &stats);
if (S_ISCHR(stats.st_mode)) {
// Looks like a tty, so we're in interactive mode.
} else if (S_ISFIFO(stats.st_mode)) {
// Looks like a pipe, so we're in non-interactive mode.
}
Of course Python is open source, so you can just look at what they do and know for sure:
http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2
On Windows you can use GetFileType.
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD type = GetFileType(hIn);
switch (type) {
case FILE_TYPE_CHAR:
// it's from a character device, almost certainly the console
case FILE_TYPE_DISK:
// redirected from a file
case FILE_TYPE_PIPE:
// piped from another program, a la "echo hello | myprog"
case FILE_TYPE_UNKNOWN:
// this shouldn't be happening...
}
Call stat() or fstat() and see if S_IFIFO is set in st_mode.
You can call stat(0, &result) and check for !S_ISREG( result.st_mode ). That's Posix, not C/C++, though.
This question already has answers here:
How do I execute a command and get the output of the command within C++ using POSIX?
(12 answers)
Closed 7 years ago.
I'm trying to start an external application through system() - for example, system("ls"). I would like to capture its output as it happens so I can send it to another function for further processing. What's the best way to do that in C/C++?
From the popen manual:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
Try the popen() function. It executes a command, like system(), but directs the output into a new file. A pointer to the stream is returned.
FILE *lsofFile_p = popen("lsof", "r");
if (!lsofFile_p)
{
return -1;
}
char buffer[1024];
char *line_p = fgets(buffer, sizeof(buffer), lsofFile_p);
pclose(lsofFile_p);
EDIT: misread question as wanting to pass output to another program, not another function. popen() is almost certainly what you want.
System gives you full access to the shell. If you want to continue using it, you can
redirect it's output to a temporary file, by system("ls > tempfile.txt"), but choosing a secure temporary file is a pain. Or, you can even redirect it through another program: system("ls | otherprogram");
Some may recommend the popen() command. This is what you want if you can process the output yourself:
FILE *output = popen("ls", "r");
which will give you a FILE pointer you can read from with the command's output on it.
You can also use the pipe() call to create a connection in combination with fork() to create new processes, dup2() to change the standard input and output of them, exec() to run the new programs, and wait() in the main program to wait for them. This is just setting up the pipeline much like the shell would. See the pipe() man page for details and an example.
The functions popen() and such don't redirect stderr and such; I wrote popen3() for that purpose.
Here's a bowdlerised version of my popen3():
int popen3(int fd[3],const char **const cmd) {
int i, e;
int p[3][2];
pid_t pid;
// set all the FDs to invalid
for(i=0; i<3; i++)
p[i][0] = p[i][1] = -1;
// create the pipes
for(int i=0; i<3; i++)
if(pipe(p[i]))
goto error;
// and fork
pid = fork();
if(-1 == pid)
goto error;
// in the parent?
if(pid) {
// parent
fd[STDIN_FILENO] = p[STDIN_FILENO][1];
close(p[STDIN_FILENO][0]);
fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
close(p[STDOUT_FILENO][1]);
fd[STDERR_FILENO] = p[STDERR_FILENO][0];
close(p[STDERR_FILENO][1]);
// success
return 0;
} else {
// child
dup2(p[STDIN_FILENO][0],STDIN_FILENO);
close(p[STDIN_FILENO][1]);
dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
close(p[STDOUT_FILENO][0]);
dup2(p[STDERR_FILENO][1],STDERR_FILENO);
close(p[STDERR_FILENO][0]);
// here we try and run it
execv(*cmd,const_cast<char*const*>(cmd));
// if we are there, then we failed to launch our program
perror("Could not launch");
fprintf(stderr," \"%s\"\n",*cmd);
_exit(EXIT_FAILURE);
}
// preserve original error
e = errno;
for(i=0; i<3; i++) {
close(p[i][0]);
close(p[i][1]);
}
errno = e;
return -1;
}
The most efficient way is to use stdout file descriptor directly, bypassing FILE stream:
pid_t popen2(const char *command, int * infp, int * outfp)
{
int p_stdin[2], p_stdout[2];
pid_t pid;
if (pipe(p_stdin) == -1)
return -1;
if (pipe(p_stdout) == -1) {
close(p_stdin[0]);
close(p_stdin[1]);
return -1;
}
pid = fork();
if (pid < 0) {
close(p_stdin[0]);
close(p_stdin[1]);
close(p_stdout[0]);
close(p_stdout[1]);
return pid;
} else if (pid == 0) {
close(p_stdin[1]);
dup2(p_stdin[0], 0);
close(p_stdout[0]);
dup2(p_stdout[1], 1);
dup2(::open("/dev/null", O_WRONLY), 2);
/// Close all other descriptors for the safety sake.
for (int i = 3; i < 4096; ++i) {
::close(i);
}
setsid();
execl("/bin/sh", "sh", "-c", command, NULL);
_exit(1);
}
close(p_stdin[0]);
close(p_stdout[1]);
if (infp == NULL) {
close(p_stdin[1]);
} else {
*infp = p_stdin[1];
}
if (outfp == NULL) {
close(p_stdout[0]);
} else {
*outfp = p_stdout[0];
}
return pid;
}
To read output from child use popen2() like this:
int child_stdout = -1;
pid_t child_pid = popen2("ls", 0, &child_stdout);
if (!child_pid) {
handle_error();
}
char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
To both write and read:
int child_stdin = -1;
int child_stdout = -1;
pid_t child_pid = popen2("grep 123", &child_stdin, &child_stdout);
if (!child_pid) {
handle_error();
}
const char text = "1\n2\n123\n3";
ssize_t bytes_written = write(child_stdin, text, sizeof(text) - 1);
char buff[128];
ssize_t bytes_read = read(child_stdout, buff, sizeof(buff));
The functions popen() and pclose() could be what you're looking for.
Take a look at the glibc manual for an example.
In Windows, instead of using system(), use CreateProcess, redirect the output to a pipe and connect to the pipe.
I'm guessing this is also possible in some POSIX way?
Actually, I just checked, and:
popen is problematic, because the process is forked. So if you need to wait for the shell command to execute, then you're in danger of missing it. In my case, my program closed even before the pipe got to do it's work.
I ended up using system call with tar command on linux. The return value from system was the result of tar.
So: if you need the return value, then not no only is there no need to use popen, it probably won't do what you want.
In this page: capture_the_output_of_a_child_process_in_c describes the limitations of using popen vs. using fork/exec/dup2/STDOUT_FILENO approach.
I'm having problems capturing tshark output with popen.
And I'm guessing that this limitation might be my problem:
It returns a stdio stream as opposed to a raw file descriptor, which
is unsuitable for handling the output asynchronously.
I'll come back to this answer if I have a solution with the other approach.
I'm not entirely certain that its possible in standard C, as two different processes don't typically share memory space. The simplest way I can think of to do it would be to have the second program redirect its output to a text file (programname > textfile.txt) and then read that text file back in for processing. However, that may not be the best way.