Creating a pipe with dup2 - c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
int pfd1[2];
int pfd2[2];
pid_t pid1, pid2, pid3;
if(pipe(pfd1)==-1) {
perror("Creazione pipe");
exit(EXIT_FAILURE);
}
if(pipe(pfd2)==-1) {
perror("Creazione pipe");
exit(EXIT_FAILURE);
}
printf("Sono il padre\n");
switch(pid1=fork()) {
printf("%d\n", pid1);
case -1: {
perror("Creazione figlio 1");
exit(EXIT_FAILURE);
}
case 0: { //figlio 1
printf("Sono il figlio 1\n");
if(dup2(pfd1[1],1)==-1) { //redirige lo stdout sul descrittore scrittura
perror("Prima redirezione");
exit(EXIT_FAILURE);
}
close(pfd1[0]);
close(pfd1[1]); //lo chiudo perchè sto redirigendo lo stdout
close(pfd2[0]);
close(pfd2[1]);
execlp("ps", "ps", "-A", "-ostat,pid", (char*) NULL);
}
}
waitpid(pid1,NULL,0);
switch(pid2=fork()) {
case -1: {
perror("Creazione figlio 2");
exit(EXIT_FAILURE);
}
case 0: { //figlio 2
printf("Sono il figlio 2\n");
if(dup2(pfd1[0],0)==-1) { //redirige lo stdin sul descrittore lettura
perror("Seconda redirezione");
exit(EXIT_FAILURE);
}
printf("Prima redirezione figlio 2\n");
if(dup2(pfd2[1],1)==-1) {
perror("Terza redirezione");
exit(EXIT_FAILURE);
}
close(pfd1[1]);
close(pfd1[0]);
close(pfd2[0]);
close(pfd2[1]);
execlp("grep", "grep", "-e", "[zZ]", (char*) NULL);
}
waitpid(pid2, NULL,0);
switch(pid3=fork()) {
case -1: {
perror("Creazione terzo figlio");
exit(EXIT_FAILURE);
}
case 0: { //figlio 3
printf("Sono il figlio 3\n");
if(dup2(pfd2[0],0)==-1) {
perror("Quarta redirezione");
exit(EXIT_FAILURE);
}
close(pfd1[0]);
close(pfd1[1]);
close(pfd2[0]);
close(pfd2[1]);
execlp("awk", "awk", "'{print $2}'", (char*) NULL);
}
}
/*padre*/
//waitpid(pid1, NULL, 0);
//waitpid(pid2, NULL, 0);
waitpid(pid3, NULL, 0);
close(pfd2[0]);
close(pfd2[1]);
close(pfd1[0]);
close(pfd1[1]);
return 0;
}
}
Hello,
I'm trying to create a pipe of shell bash commands using the system call dup2.
The output I expect should be the same as
bash $> ps -A -ostat,pid | grep -e [zZ] | awk '{print $2}'
what I do is forking 3 children and make them communicate through two pipes. Each one of the children executes one part of the command.
The problem is my program gets stuck on the second child which apparently doesn't even get to do the exec.
I'm sure there are some problems with my code, but since it's the first time for me trying to use dup2 I'm a bit confused.
Also, don't mind the printfs, they're just for debugging.
Thanks a lot!

Basically you have to close pipes in the right positions and you failed to close a bracket in the switch statement. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h> /*lib for waitpid()*/
int main(void) {
int pfd1[2];
int pfd2[2];
pid_t pid1, pid2, pid3;
if(pipe(pfd1)==-1) {
perror("Creazione pipe");
exit(EXIT_FAILURE);
}
if(pipe(pfd2)==-1) {
perror("Creazione pipe");
exit(EXIT_FAILURE);
}
printf("Sono il padre\n");
switch(pid1=fork()) {
printf("%d\n", pid1);
case -1: {
perror("Creazione figlio 1");
exit(EXIT_FAILURE);
}
case 0: { //figlio 1
printf("Sono il figlio 1\n");
if(dup2(pfd1[1],1)==-1) { //redirige lo stdout sul descrittore scrittura
perror("Prima redirezione");
exit(EXIT_FAILURE);
}
close(pfd1[0]);
close(pfd1[1]); //lo chiudo perchè sto redirigendo lo stdout
close(pfd2[0]);
close(pfd2[1]);
execlp("ps", "ps", "-A", "-ostat,pid", (char*) NULL);
}
}
waitpid(pid1,NULL,0);
switch(pid2=fork()) {
case -1: {
perror("Creazione figlio 2");
exit(EXIT_FAILURE);
}
case 0: { //figlio 2
printf("Sono il figlio 2\n");
if(dup2(pfd1[0],0)==-1) { //redirige lo stdin sul descrittore lettura
perror("Seconda redirezione");
exit(EXIT_FAILURE);
}
printf("Prima redirezione figlio 2\n");
if(dup2(pfd2[1],1)==-1) {
perror("Terza redirezione");
exit(EXIT_FAILURE);
}
close(pfd1[0]);
close(pfd1[1]);
close(pfd2[0]);
close(pfd2[1]);
execlp("grep", "grep", "-e", "[Ss]", (char*) NULL);
}
} /*You forgot that bracket*/
/*
* Close the pipe before waitpid because 2rd child
* will wait until you close it. Check pipe theory
*/
close(pfd1[1]);
close(pfd1[0]);
waitpid(pid2, NULL,0);
switch(pid3=fork()) {
case -1: {
perror("Creazione terzo figlio");
exit(EXIT_FAILURE);
}
case 0: { //figlio 3
printf("Sono il figlio 3\n");
if(dup2(pfd2[0],0)==-1) {
perror("Quarta redirezione");
exit(EXIT_FAILURE);
}
close(pfd2[0]);
close(pfd2[1]);
/*' ' removed from the "{print $2}"*/
execlp("awk", "awk", "{print $2}", (char*) NULL);
}
}
/*
* Close the pipe before waitpid because 3rd child
* will wait until you close it. Check pipe theory
*/
close(pfd2[0]);
close(pfd2[1]);
waitpid(pid3, NULL, 0);
return 0;
}

Related

Sort users' shells

I am trying to this in C - get the users' in /etc/passwd their shells, sort them and then use uniq -c to see how many times a shell is used.
cat /etc/passwd | cut -d ':' -f 7 | sort | uniq -c
The output should be sth like that:
94 /bin/bash
1 /bin/sync
27 /usr/sbin/nologin
What I have done:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
int main(int argc, char* argv[]){
int a[2];
if((pipe(a))==-1){
printf("error in creating a pipe");
exit(-1);
}
pid_t p = fork();
if(p==-1){
printf("error in fork");
exit(-1);
}
if(p==0){
close(a[0]);
dup2(a[1], 1);
execlp("cat", "cat", "/etc/passwd", NULL);
}
int b[2];
if((pipe(b))==-1){
printf("error in creating the second pipe");
exit(-1);
}
pid_t k=fork();
if(k==-1){
printf("error in the 2nd fork");
exit(-1);
}
if(k==0){
close(b[0]);
dup2(a[0], 0);
dup2(b[1], 1);
execlp("cut", "cut", "-d:", "-f", "7", NULL);
}
int c[2];
if((pipe(c))==-1){
printf("error in the 3rd pipe");
exit(-1);
}
pid_t l=fork();
if(l==-1){
printf("error in 3rd fork");
exit(-1);
}
if(l==0){
close(c[0]);
dup2(b[0], 0);
dup2(c[1], 1);
execlp("sort", "sort", NULL);
}
close(c[1]);
dup2(c[0], 0);
execlp("uniq", "uniq", "-c", NULL);
exit(0);
}
and when I compile it and start it, it looks like it does not terminate, the propt just stays empty and I have to use cntrl+c to stop the program.
You have to close all these open pipes.
if(p==0){
close(a[0]);
dup2(a[1], 1);
execlp("cat", "cat", "/etc/passwd", NULL);
}
close(a[1];
...
if(k==0){
close(b[0]);
dup2(a[0], 0);
dup2(b[1], 1);
execlp("cut", "cut", "-d:", "-f", "7", NULL);
}
close(a[0]);
close(b[1]);
...
if(l==0){
close(c[0]);
dup2(b[0], 0);
dup2(c[1], 1);
execlp("sort", "sort", NULL);
}
close(b[0]);
close(c[1]);
...
the problem is that there are a bunch of pipes that aren't properly closed. This is why read() calls block.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
char buf[1024];
int main(int argc, char* argv[]){
int a[2];
if((pipe(a))==-1){
fprintf(stderr, "error in creating a pipe");
exit(-1);
}
pid_t p = fork();
if(p==-1){
fprintf(stderr, "error in fork");
exit(-1);
}
if(p==0){
fprintf(stderr, "starting cat (%d)\nstdout = a[1], stdin = closed\n", p);
close(a[0]);
dup2(a[1], 1);
execlp("cat", "cat", "/etc/passwd", NULL);
}
close(a[1]); /* the parent process is not going to write to a[1] */
int b[2];
if((pipe(b))==-1){
fprintf(stderr, "error in creating the second pipe");
exit(-1);
}
pid_t k=fork();
if(k==-1){
fprintf(stderr, "error in the 2nd fork");
exit(-1);
}
if(k==0){
fprintf(stderr, "starting cut (%d)\nstdout = b[1], stdin = a[0]\n", k);
close(b[0]);
dup2(a[0], 0);
dup2(b[1], 1);
execlp("cut", "cut", "-d:", "-f", "7", NULL);
}
close(b[1]);
int c[2];
if((pipe(c))==-1){
fprintf(stderr, "error in the 3rd pipe");
exit(-1);
}
pid_t l=fork();
if(l==-1){
fprintf(stderr, "error in 3rd fork");
exit(-1);
}
if(l==0){
fprintf(stderr, "starting sort (%d)\nstdout = c[1], stdin = b[0]\n", l);
close(c[0]);
dup2(b[0], 0);
dup2(c[1], 1);
execlp("sort", "sort", NULL);
}
fprintf(stderr, "Exec uniq\nstdout = stdout, stdin = c[0]\n");
close(c[1]);
dup2(c[0], 0);
execlp("uniq", "uniq", "-c", NULL);
exit(0);
}

C program using pipes hangs when trying to terminate

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#define MSGSIZE 64
char msgbuf[MSGSIZE];
int main() {
int p1[2];
int p2[2];
int nread;
int choice = 0;
pid_t child_a, child_b;
if (pipe(p1) == -1) {
printf("error in creating pipe\n");
exit(-1);
}
if (pipe(p2) == -1) {
printf("error in creating pipe\n");
exit(-1);
}
child_a = fork();
if (child_a == 0) {
while (1) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %s\n", getpid(), msgbuf);
close(p1[0]);
close(p1[1]);
}
} else {
child_b = fork();
if (child_b == 0) {
while (1) {
dup2(p2[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %s\n", getpid(), msgbuf);
close(p2[0]);
close(p2[1]);
}
} else {
while (1) {
printf("<child_to_receive_msg> <message>\n");
scanf("%d %s", &choice, msgbuf);
switch (choice) {
case 1:
usleep(250);
write(p1[1], msgbuf, MSGSIZE);
break;
case 2:
usleep(250);
write(p2[1], msgbuf, MSGSIZE);
break;
default:
printf("Process does not exist");
break;
case -1:
close(p1[0]);
close(p2[0]);
printf("parent waiting");
wait(NULL);
exit(0);
}
}
}
}
return 0;
}
In the above program I have a parent making two child processes belonging to that same parent. The user writes to the parent process which pipes the message to be read by either child 1 or child 2. It keeps doing this continuously unless the user inputs -1.
The problem is that case in my switch statement doesn't get executed and instead the program hangs. I think I have my pipes closed at the correct places.
You need to send some signal to your child process to inform then to terminate before waiting for them to exit. You should define some pre-defined message which means its time for child to terminate. Check below code. Here pre-defined message is "-1". You should choose your own which doesn't conflict with your application's real data.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define MSGSIZE 64
char msgbuf[MSGSIZE];
int main() {
int p1[2];
int p2[2];
int nread;
int choice = 0;
pid_t child_a, child_b;
if (pipe(p1) == -1) {
printf("error in creating pipe\n");
exit(-1);
}
if (pipe(p2) == -1) {
printf("error in creating pipe\n");
exit(-1);
}
child_a = fork();
if (child_a == 0) {
while (1) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %s\n", getpid(), msgbuf);
close(p1[0]);
close(p1[1]);
if (strcmp(msgbuf, "-1") == 0) { // check if time to end
break;
}
}
} else {
child_b = fork();
if (child_b == 0) {
while (1) {
dup2(p2[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
printf("%d receives message: %s\n", getpid(), msgbuf);
close(p2[0]);
close(p2[1]);
if (strcmp(msgbuf, "-1") == 0) { // check if time to end
break;
}
}
} else {
while (1) {
printf("<child_to_receive_msg> <message>\n");
scanf("%d %s", &choice, msgbuf);
switch (choice) {
case 1:
usleep(250);
write(p1[1], msgbuf, MSGSIZE);
break;
case 2:
usleep(250);
write(p2[1], msgbuf, MSGSIZE);
break;
default:
printf("Process does not exist\n");
break;
case -1:
strcpy(msgbuf, "-1");
write(p1[1], msgbuf, MSGSIZE); // send message to end
close(p1[0]);
close(p2[0]);
printf("parent waiting\n");
wait(NULL);
exit(0);
}
}
}
}
return 0;
}
First, you need to start performing error checking. Check the man page of the calls you make. Add checks in your code to detect errors. When they return an error, use perror and exit(EXIT_FAILURE);.
Second, you need to start paying attention to the values returned by read and write since they could be less than expected. These need to be called in a loop.
For example, for read, you'd use the following:
#include <errno.h>
#include <limits.h>
// Returns the number of bytes read.
// EOF was reached if the number of bytes read is less than requested.
// On error, returns -1 and sets errno.
ssize_t read_fixed_amount(int fd, char *buf, size_t size) {
if (size > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
ssize_t bytes_read = 0;
while (size > 0) {
ssize_t rv = read(fd, buf, size);
if (rv < 0)
return -1;
if (rv == 0)
return bytes_read;
size -= rv;
bytes_read += rv;
buf += rv;
}
return bytes_read;
}
It would be used something like this:
ssize_t bytes_read = read_fixed_amount(fd, buf, size);
if (bytes_read < 0) {
perror("read");
exit(EXIT_FAILURE);
}
if (bytes_read == 0) {
printf("EOF reached\n");
exit(EXIT_SUCCESS);
}
if (bytes_read != size) {
fprintf(stderr, "read: Premature EOF.\n");
exit(EXIT_FAILURE);
}
Third, reading from the pipe will only return EOF once all file descriptors of the write end of the pipes have been closed.
Right after the fork, the parent should do
close(p1[0]);
close(p2[0]);
Right after the fork, child 1 should do
close(p1[1]);
close(p2[0]);
close(p2[1]);
Right after the fork, child 2 should do
close(p1[0]);
close(p1[1]);
close(p2[1]);
Fourth, there's this monstrosity:
while (1) {
dup2(p1[0], STDIN_FILENO);
read(STDIN_FILENO, msgbuf, MSGSIZE);
...
close(p1[0]);
close(p1[1]);
}
Really? Infinite loop. Attempt to repeatedly make STDIN a dup of p1[0]. Duping of a closed descriptor.
This should appear before the loop:
dup2(p1[0], STDIN_FILENO);
close(p1[0]);
Or you could skip those two call and simply read from p1[0] instead of STDIN_FILENO.
As for the infinite loop, it goes back to the second point. Check the value returned by read.
Fifth, you only wait for one child to finish, but there are two children to wait for. You need to call wait twice.

Problem with progamming an intern comands in a shell interpeter (made by myself)

I had a project of operating systems where I had to program a command interpreter in shell in C. I've done it most part of it, the code I've already implemented allows to execute simple commands like ls, or commands in background and/or with pipes.
But now I also have to program an intern command (a new one). Its name is going to be myTime and it should allow you to measure the time that takes to a process to execute its function. For example if you execute the command sleep 10 in the Linux shell, the process should sleep at least the time indicated. The goal of the intern command myTime is to measure that time.
IMPORTANT: In the code I've been giving some help from the teachers that allows me with a function called obtain_orden obtain the command introduced (ex: calling argvv[i][0] for the i command introduced), the arguments (argvv[i][1], the first argument), and if it is or not in background (bg =0/1)
Also, the function pid_t fork() creates a new process or a new child process and returns 0 if it's the child or pid if it's the parent.
I have additional code to compile etc, if you need it or its something nogt clear tell me. Thanks!
I´ve been searching in the sys/time.h library and the function gettimeofday. But i haven't been able to show the message I'm required to in my project: "Time spent: %f secs.\n"
As this intern command should measure the time that takes to a process to execute I've been told that I should execute a fork() and an exec(), and measure the time once the child process has done its function.
It should show like this:
msh> mytime sleep 5
Time spent: 5.001719 secs
msh>
extern int obtain_order(); /* See parser.y for description */
int main(void)
{
char ***argvv;
//int command_counter;
int num_commands;
//int args_counter;
char *filev[3];
int bg;
int ret;
setenv("Acc","0",1);
setbuf(stdout, NULL); /* Unbuffered */
setbuf(stdin, NULL);
while (1)
{
fprintf(stderr, "%s", "msh> "); /* Prompt */
ret = obtain_order(&argvv, filev, &bg);
if (ret == 0) break; /* EOF */
if (ret == -1) continue; /* Syntax error */
num_commands = ret - 1; /* Line */
if (num_commands == 0) continue; /* Empty line */
if(num_commands==1){
//Implementación propia para salir del mini-shell
if(strcmp(argvv[0][0], "exit") == 0){
exit(0);
}
if(strcmp(argvv[0][0],"myTime")==0){
//HERE IS WHERE I HAVE TO IMPLEMENT THE INTERN COMMAND MYTIME
}else{ //For all other commands ls, etc... (with pipes (max 3)and background)
int pid;
int estado;
pid = fork();
switch(pid) {
case -1: /* error */
fprintf(stderr, "%s","Error en el fork del mandato simple\n");
return (-1);
case 0: /* hijo */
if (filev[0] != NULL) {
close(STDIN_FILENO);
open(filev[0],O_RDONLY);
}
if (filev[1] != NULL) {
close(STDOUT_FILENO);
open(filev[1],O_CREAT|O_WRONLY,0666);
}
if (filev[2] != NULL) {
close(STDERR_FILENO);
open(filev[2],O_CREAT|O_WRONLY,0666);
}
execvp(argvv[0][0], argvv[0]);
fprintf(stderr, "%s","Error en el execvp del mandato simple\n");
return(-1);
default: /* padre */
if(!bg){
while (wait(&estado) != pid);
}else printf("%d\n",pid);
} //fin switch (1 mandato)
} //fin if (si es comando interno o no)
}else if(num_commands==2){
int pid;
int estado;
int fd[2];
pipe(fd);
pid = fork();
switch(pid) {
case -1: /* error */
fprintf(stderr, "%s","Error en el fork del primer mandato\n");
return (-1);
case 0: /* hijo1 */
close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
if (filev[0] != NULL) {
close(STDIN_FILENO);
open(filev[0],O_RDONLY);
}
if (filev[2] != NULL) {
close(STDERR_FILENO);
open(filev[2],O_CREAT|O_WRONLY,0666);
}
execvp(argvv[0][0], argvv[0]);
fprintf(stderr, "%s","Error en el execvp del primer mandato\n");
return(-1);
default: /* padre */
pid = fork();
switch(pid) {
case -1: /* error */
fprintf(stderr, "%s","Error en el fork del segundo mandato\n");
return (-1);
case 0: /* hijo 2*/
close(STDIN_FILENO);
dup(fd[0]);
close(fd[0]);
close(fd[1]);
if (filev[1] != NULL) {
close(STDOUT_FILENO);
open(filev[1],O_CREAT|O_WRONLY,0666);
}
if (filev[2] != NULL) {
close(STDERR_FILENO);
open(filev[2],O_CREAT|O_WRONLY,0666);
}
execvp(argvv[1][0], argvv[1]);
fprintf(stderr, "%s","Error en el execvp del segundo mandato\n");
return(-1);
default: /* padre */
close(fd[0]);
close(fd[1]);
if(!bg){
while (wait(&estado) != pid);
}else printf("%d\n",pid);
} //fin switch2 (2 mandatos)
} //fin switch1 (2 mandatso)
}else if(num_commands==3){
int pid;
int estado;
int fd[2], fd2[2];
pipe(fd);
pid = fork();
switch(pid) {
case -1: /* error */
fprintf(stderr, "%s","Error en el fork del primer mandato\n");
return (-1);
case 0: /* hijo1 */
close(STDOUT_FILENO);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
if (filev[0] != NULL) {
close(STDIN_FILENO);
open(filev[0],O_RDONLY);
}
if (filev[2] != NULL) {
close(STDERR_FILENO);
open(filev[2],O_CREAT|O_WRONLY,0666);
}
execvp(argvv[0][0], argvv[0]);
fprintf(stderr, "%s","Error en el execvp del primer mandato\n");
return(-1);
default: /* padre */
pipe(fd2);
pid = fork();
switch(pid) {
case -1: /* error */
fprintf(stderr, "%s","Error en el fork del segundo mandato\n");
return (-1);
case 0: /* hijo 2*/
close(STDIN_FILENO);
dup(fd[0]);
close(STDOUT_FILENO);
dup(fd2[1]);
close(fd[0]);
close(fd[1]);
close(fd2[0]);
close(fd2[1]);
if (filev[2] != NULL) {
close(STDERR_FILENO);
open(filev[2],O_CREAT|O_WRONLY,0666);
}
execvp(argvv[1][0], argvv[1]);
fprintf(stderr, "%s","Error en el execvp del segundo mandato\n");
return(-1);
default: /* padre */
close(fd[0]);
close(fd[1]);
pid = fork();
switch(pid) {
case -1: /* error */
fprintf(stderr, "%s","Error en el fork del tercer mandato\n");
return (-1);
case 0: /* hijo 3*/
close(STDIN_FILENO);
dup(fd2[0]);
close(fd2[0]);
close(fd2[1]);
if (filev[1] != NULL) {
close(STDOUT_FILENO);
open(filev[1],O_CREAT|O_WRONLY,0666);
}
if (filev[2] != NULL) {
close(STDERR_FILENO);
open(filev[2],O_CREAT|O_WRONLY,0666);
}
execvp(argvv[2][0], argvv[2]);
fprintf(stderr, "%s","Error en el execvp del tercer mandato\n");
return(-1);
default: /* padre */
close(fd2[0]);
close(fd2[1]);
if(!bg){
while (wait(&estado) != pid);
}else printf("%d\n",pid);
} //fin switch 3 (3 mandatos)
} //fin switch2 (3 mandatos)
} //fin switch1 (tres mandatos)
} //fin if (número de mandatos)
} //fin while
return 0;
} //fin main

Piping between multiple child processes

I'm trying to process input through different child process from one parent. I can make it though the first 3 children, but after that, I can't seem to get any input into or anything of out sort.
Here is my code.
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
pid_t pid;
int pipes_1[2];
int pipes_2[2];
pipe(pipes_1);
pipe(pipes_2);
switch(pid=fork())
{
case 0:
dup2(pipes_1[0], 0);//copy stdin onto pipe 1 read
dup2(pipes_2[1], 1);//copy stdout onto pipe 2 write
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("sed", "sed","s/[^a-zA-Z]/ /g", "test.txt", (char*)NULL);
break;
default:
break;
}
switch(pid = fork())
{
case 0:
dup2(pipes_2[0],0); //copy std onto pipes 2 read
dup2(pipes_1[1],1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("tr", "tr", "[A-Z]", "[a-z]", (char*)NULL);
break;
default:
break;
}
switch(pid=fork())
{
case 0:
dup2(pipes_1[0], 0);
dup2(pipes_2[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("awk", "awk", "{for(i = 1; i <= NF; i++) {print $i;}}", (char*)NULL);
break;
default:
break;
}
switch(pid=fork())
{
case 0:
dup2(pipes_2[0], 0);
//dup2(pipes_1[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("sort", "sort", (char*)NULL);
default:
break;
}
wait();
return 0;
}
I know the formatting is bad. But can you see what I'm doing wrong? I think it may have something to do with only using two pipes.
EDIT: Updated code with 4 pipes.
int main(int argc, char** argv)
{
pid_t pid;
int pipes_1[2];
int pipes_2[2];
int pipes_3[2];
int pipes_4[2];
pipe(pipes_1);
pipe(pipes_2);
pipe(pipes_3);
pipe(pipes_4);
switch(pid=fork())
{
case 0:
dup2(pipes_1[0], 0);//copy stdin onto pipe 1 read
dup2(pipes_2[1], 1);//copy stdout onto pipe 2 write
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("sed", "sed","s/[^a-zA-Z]/ /g", "test.txt", (char*)NULL);
break;
default:
break;
}
switch(pid = fork())
{
case 0:
dup2(pipes_2[0],0); //copy std onto pipes 2 read
dup2(pipes_3[1],1);
close(pipes_3[0]);
close(pipes_3[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("tr", "tr", "[A-Z]", "[a-z]", (char*)NULL);
break;
default:
break;
}
switch(pid=fork())
{
case 0:
dup2(pipes_3[0], 0);
dup2(pipes_4[1], 1);
close(pipes_3[0]);
close(pipes_3[1]);
close(pipes_4[0]);
close(pipes_4[1]);
execlp("awk", "awk", "{for(i = 1; i <= NF; i++) {print $i;}}", (char*)NULL);
break;
default:
break;
}*/
switch(pid=fork())
{
case 0:
dup2(pipes_4[0], 0);
//dup2(pipes_1[1], 1);
close(pipes_4[0]);
close(pipes_4[1]);
//close(pipes_2[0]);
//close(pipes_2[1]);
execlp("sort", "sort", (char*)NULL);
default:
break;
}
wait();
return 0;
}
You seem to have a pipeline:
sed … | tr … | awk … | sort
You create just two pipes, where three are needed. Create the third pipe and handle it correctly, and you'll be OK.
Adapting second code. Note that only three pipes are needed for four processes (and in general N-1 pipes are needed for N processes).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
static inline void error(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int pid;
int pipes_1[2];
int pipes_2[2];
int pipes_3[2];
pipe(pipes_1);
pipe(pipes_2);
pipe(pipes_3);
if ((pid = fork()) == 0)
{
dup2(pipes_1[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("sed", "sed", "s/[^a-zA-Z]/ /g", "test.txt", (char *)NULL);
error("Failed to exec sed");
}
printf("sed: %d\n", pid);
if ((pid = fork()) == 0)
{
dup2(pipes_1[0], 0);
dup2(pipes_2[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("tr", "tr", "[A-Z]", "[a-z]", (char *)NULL);
error("Failed to exec tr");
}
printf("tr: %d\n", pid);
if ((pid = fork()) == 0)
{
dup2(pipes_2[0], 0);
dup2(pipes_3[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("awk", "awk", "{for(i = 1; i <= NF; i++) {print $i;}}", (char *)NULL);
error("Failed to exec awk");
}
printf("awk: %d\n", pid);
if ((pid = fork()) == 0)
{
dup2(pipes_3[0], 0);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("sort", "sort", (char *)NULL);
error("Failed to exec sort");
}
printf("sort: %d\n", pid);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
printf("PID %d died 0x%.4X\n", corpse, status);
return 0;
}
Sample input:
Happy Go Lucky!
PENULTIMATE DESTINY#
missing all upper-case=
What gives?
Digital 023123098 Diarrhea
Sample output:
sed: 74841
tr: 74842
awk: 74843
sort: 74844
PID 74841 died 0x0000
PID 74842 died 0x0000
PID 74843 died 0x0000
all
case
destiny
diarrhea
digital
gives
go
happy
lucky
missing
penultimate
upper
what
PID 74844 died 0x0000
The process ID information is primarily diagnostic. If you pipe the output of the program to a filter, you will get different output (because of buffering, etc), but the difference is in the sequence in which the sorted data appears vs the diagnostics. Print the diagnostics to standard error, or add a fflush(stdout) after each printf() and you'll get something similar to the shown output more routinely.

Pipe Unix commands together with C

I want to simulate this Unix command :
cat file.txt | sort | tail -4
I followed the technique, but it does not work, it remains blocked.
Maybe i need to use something else when there are files.
I used two pipes, and two processes and i used two DUP in a single process, maybe that's wrong.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
int p1[2];
int p2[2];
if(pipe(p1))
{
perror("pipe1");
exit(0);
}
if(pipe(p2))
{
perror("pipe2");
exit(0);
}
switch(fork())
{
case -1: perror(" fork1 error ");
exit(0);
case 0: close(STDOUT_FILENO);
(void)dup(p1[1]);
close(p1[1]);
close(p1[0]);
execlp("cat", "cat", "file.txt", NULL);
exit(0);
default:
switch(fork())
{
case -1: perror(" fork2 error ");
exit(0);
case 0: close(STDIN_FILENO);
(void)dup(p1[0]);
close(p1[1]);
close(p1[0]);
close(STDOUT_FILENO);
(void)dup(p2[1]);
close(p2[1]);
close(p2[0]);
execlp("sort", "sort", NULL);
exit(0);
default:
wait(NULL);
close(STDIN_FILENO);
(void)dup(p2[0]);
close(p2[0]);
close(p2[1]);
execlp("tail", "tail", "-4", NULL);
}
}
}
this is the file.txt :
g
f
d
b
c
a
e
The parent process never closes the pipe p1 so its child keep trying to read on it. Add close(p1[0]); close(p1[1]); before the execlp("tail", "tail", "-4", NULL);.
Also note that you should not wait(NULL): this is another hang waiting to happen when file.txt is big and starts to fill the pipe buffer.

Resources