This question already has answers here:
Implementing pipe in C
(2 answers)
How to send a simple string between two programs using pipes?
(7 answers)
Closed 3 years ago.
I want to learn how to use pipes in C, and tried to do basic things like for example cloning the behaviour of | in shell.
This is my first try:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
FILE *stdin_tmp;
stdin_tmp = stdin;
stdin = stdout;
system("cat /tmp/test.txt");
system("less");
stdin = stdin_tmp;
return 0;
}
This is what I want to do (written in shell):
cat /tmp/test.txt |less
The behaviour is obviously not what I expected. less isn't receiving the output of cat.
How is it done correctly?
Try the popen() function.
Here's the prototype for it:
FILE *popen(const char *command, const char *type);
And here's a correct way to use it:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int ch;
FILE *input;
FILE *output;
input = popen("cat /tmp/test.txt", "r");
output = popen("less", "w");
if (!input || !output)
return EXIT_FAILURE;
while( (ch = fgetc(input)) != EOF )
fputc(ch, output);
pclose(input);
pclose(output);
return 0;
}
Related
I need to read output of a program that uses carriage returns (\r) to make its output.
I have an old utility with output like this:
#include <stdio.h>
#include <unistd.h>
int main() {
setvbuf(stdout, (char*)NULL,_IONBF, 0);
fprintf(stdout, "Start\n");
for (auto i = 0; i < 100; ++i) {
fprintf(stdout, "\r%2d", i);
usleep(100000);
}
fprintf(stdout, "\nEnd\n");
return 0;
}
So I execute it with popen from my program and I need to trace a progress:
#include <memory>
#include <stdio.h>
int main(int argc, char* argv[]) {
if (argc != 2) {
return -1;
}
std::unique_ptr<FILE, int(*)(FILE*)> filePtr( popen(argv[1], "r"), &pclose );
int c = fgetc(filePtr.get());
while (c != EOF); {
printf("%c", c);
c = fgetc(filePtr.get());
}
return 0;
}
This work fine only without \r in stdout. Does anybody know is it possible to execute a process and read all output even with carriaage return symbols?
The problem here is that the carriage-return '\r' isn't really anything special, it's how you handle it that makes it different.
A terminal program uses it to control cursor position, but that's only done inside the terminal program itself, it's not "built-in" in the '\r' character.
If you want to handle it in a special way then you need to create your program that way.
The manpage for popen says "reading from a "popened" stream reads the command's standard output".
However, I can't seem to get the subprocess output in the trivial program below. The "reader" parent process blocks on the read (whether using fgets or fread)
What am I missing?
Attaching to the pinger program with gdb shows it is looping and calling printf to output text. Just nothing detected by fgets on the parent's side...
PINGER.C
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i = 0;
while (1)
{
printf("stdout %d\n", i++);
sleep(1);
}
}
POPENTEST.C
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
int
main(int argc, char **argv)
{
char *cmd = "./pinger";
printf("Running '%s'\n", cmd);
FILE *fp = popen(cmd, "r");
if (!fp)
{
perror("popen failed:");
exit(1);
}
printf("fp open\n");
char inLine[1024];
while (fgets(inLine, sizeof(inLine), fp) != NULL)
{
printf("Received: '%s'\n", inLine);
}
printf("feof=%d ferror=%d: %s\n", feof(fp), ferror(fp), strerror(errno));
pclose(fp);
}
OUTPUT
$ ./popenTest
fp open
By default, C buffers writes to the stdout when stdout is not connected to a tty. This means that from the OS' perspective, the program has not written anything to stdout until either the buffer is full or you manually flushed the output:
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int i = 0;
while (1)
{
printf("stdout %d\n", i++);
fflush(stdout);
sleep(1);
}
}
When connected to a tty, the stdout is automatically flushed on every newline. But this automatic flushing does not happen when the stdout is connected to a pipe.
This question already has an answer here:
Flushing buffers in C
(1 answer)
Closed 3 years ago.
I am trying to simulate a race condition (is this a correct term?) in order to fix it with semaphores afterward.
I have a master.c process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
for(int i = 0; i < 100; ++i)
{
if (fork() == 0)
{
char str[12];
sprintf(str, "%d", i%10);
execl("./slave", "slave", str, (char *)0);
}
}
return 0;
}
And a slave.c process which prints into a file:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
FILE *p_file;
p_file = fopen("out.txt", "a");
for (int i = 0; i < 100; ++i)
{
usleep(100000); // I tried using this but it changes nothing
fprintf(p_file, "%s", argv[1]);
}
fprintf(p_file, "\n");
return 0;
}
The output file out.txt looks like this:
https://pastebin.com/nU6YsRsp
The order is "random", but no data is not corrupted for some reason. Why is that?
Because stdio uses buffered output by default when writing to a file, and everything you're printing in each process fits into a single buffer. The buffer doesn't get flushed until the process exits, and then it's written as a single write() call, which is small enough to be written atomically to the file.
Call fflush(p_file); after each fprintf() and you'll get more mixed up results. Or call setvbuf() to disable buffering.
I am trying to write a simple C PROGAM which EXECUTE a Python SCRIPT (and let it running...) and closes itself.
I tried the following commands but in both cases the C PROGRAM is still alive...
popen("sudo python /home/pi/main.py", "r");
system("sudo python /home/pi/main.py");
Thanks!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Edited !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
I tried this command based on your comments but no success:
char *argv[] = {"/home/pi/main.py"};
execv("sudo python", argv);
Anyone could help? Thanks!
!!!!!!!!!!! Edit 2 !!!!!!!!!!!!!!
This is how I compile it:
gcc -Wall restart.c -o safekill
This is the C program
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
my_popen(char cmd[])
{
FILE *fp;
char path[1035];
fp = popen(cmd, "r");
if (fp == NULL)
{
printf("Failed to run command\n");
exit(1);
}
//Read the output a line at a time - output it
while (fgets(path, sizeof(path)-1, fp) != NULL)
{
printf("%s", path);
}
pclose(fp);
}
int main()
{
my_popen("sudo killall python");
sleep(1);
my_popen("sudo killall raspivid");
sleep(1);
if(fork())
printf("Am I here?");
return 0;
char *file = "restart";
char *argv[] = {file, "-c", "sudo python main.py", NULL};
execvp(file, argv);
}
Result: It prints am I here and doesn't start the python.
It is so frustrating.... :-(
You need to add the filename of the program itself to the argument list (argv[0]) and terminate the argument list with a NULL pointer.
Example:
#include <stdlib.h>
#include <unistd.h>
int main() {
if(fork())
return 0;
char *file = "python";
char *argv[] = {file, "-c", "import time; time.sleep(5); print 'Hello'", NULL};
execvp(file, argv);
}
Expected behavior: Immediate (parent) program termination and a short Hello printed 5 seconds later by the child.
Maybe you need to workaround the sudo somehow, but this should get you started.
I need to get the file from the terminal, I know the command will look like:
./a.out < fileName.txt
I'm not sure how to use fgets() in my program to use the file requested from the terminal.
Using redirection sends the contents of the input file to stdin, so you need to read from stdin inside your code, so something like (error checking omitted for clarity)
#include <stdio.h>
#define BUFFERSIZE 100
int main (int argc, char *argv[])
{
char buffer[BUFFERSIZE];
fgets(buffer, BUFFERSIZE , stdin);
printf("Read: %s", buffer);
return 0;
}
1.) you close stdin then assign a different file handler to it
2.) replace stdin with any other file handler
using dup2 function you can achieve it
Short Answer
open() your file, then dup2() your file descriptor towards Standard Input.
A dummy example
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char *command[] = {"/usr/bin/sort", NULL};
if (close(STDIN_FILENO) < 0)
{
perror("Error close()");
exit(EXIT_FAILURE);
}
if (fd = open(argv[2], O_RDONLY, S_IWUSR | S_IRUSR) < 0)
{
perror("Error open()");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDIN_FILENO) < 0)
{
perror("Error dup2()");
exit(EXIT_FAILURE);
}
if (execv(command[0], command) < 0)
{
perror("Error execv()");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Output:
$ ./a.out < JohnLennon_Imagine_Lyrics.txt
Above us, only sky
A brotherhood of man
Ah
And no religion, too
And the world will be as one
And the world will live as one
But I'm not the only one
But I'm not the only one
I hope someday you'll join us
I hope someday you'll join us
Imagine all the people
Imagine all the people
Imagine all the people
Imagine no possessions
Imagine there's no countries
Imagine there's no heaven
It isn't hard to do
It's easy if you try
I wonder if you can
Livin' for today
Livin' life in peace
No hell below us
No need for greed or hunger
Nothing to kill or die for
Sharing all the world
You
You
You may say I'm a dreamer
You may say I'm a dreamer
You can use fread function
#include <stdio.h>
#include <malloc.h>
int main () {
fseek(stdin, 0, SEEK_END);
long size = ftell(stdin);
rewind(stdin);
char *buffer = (char*) malloc(size);
fread(buffer, size, 1, stdin);
printf("Buffer content:\n%s\n", buffer);
return 0;
}