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.
Related
(Please see my final code below; working version after visiting professor)
Program sets up a child-TO-parent pipe; the child proceeds with an 'exec' to perform a "pre" process and its output is connected to the pipe connected to the parent, which proceeds with an 'exec' to perform a "sort" process.
CHANGES SO FAR: The code is still freezing. I changed my exec() calls and made other changes too.
Not sure where the issue lies.
First revision
/*
Student: Douglas Adolph
Course: Operating Systems
Project #: 1: Part2_childToParent.c
*/
/* The second example of pipe, between a parent and a child*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#define MSGSIZE 512
int main()
{
char inbuf[MSGSIZE];
int p[2];
pid_t pid;
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch(pid = fork()) {
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup2(p[1], STDOUT_FILENO); // redirect standard output and error output
close(p[0]); // close link
close(p[1]);
execlp("./pre", "pre",(char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
close(p[1]);
dup2(p[0], STDIN_FILENO); // redirect standard input and error input
execlp("./sort", "sort",(char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
return(EXIT_FAILURE);
}
Next revision
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define MSGSIZE 512
int main()
{
char inbuf[MSGSIZE];
int p[2];
pid_t pid;
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch(pid = fork()) {
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup(p[1]); // redirect standard output and error output
close(p[0]); // close link
//close(p[1]);
execl("./pre", "pre",(char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
wait((int *)0);
close(p[1]);
if ((n = read(p[0], inbuf, MSGSIZE)) >= 0) {
printf("in buff");
inbuf[n] = 0; // terminate the string
printf("%s\n", inbuf); // print message
}
execl("./sortC", "sortC",(char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
return(EXIT_FAILURE);
}
Final code after visiting professor, confirming requirements, and running code successfully:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#define MSGSIZE 512
int main() {
char inbuf[MSGSIZE];
int p[2];
pid_t pid;
if (pipe(p) == -1) {
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch(pid = fork()) {
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
close(1);
dup(p[1]); // redirect standard output and error output
close(p[0]);
close(p[1]);
execl("./pre", "pre",(char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
execl("./sort", "sort", (char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
return(EXIT_FAILURE);
}
Round 1
Here's a mildly modified version of your updated code — tagged 'first revision' in the question.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MSGSIZE 512
int main(void)
{
//char inbuf[MSGSIZE];
int p[2];
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch (fork())
{
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup2(p[1], STDOUT_FILENO); // redirect standard output and error output
close(p[0]); // close link
close(p[1]);
execl("./preC", "preC", (char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
close(p[1]);
dup2(p[0], STDIN_FILENO);
close(p[0]);
// read(p[0], inbuf, MSGSIZE); // read message in pipe (sent from child)
// printf("%s\n", inbuf); // print message
execl("./sortC", "sortC", (char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
/*NOTREACHED*/
return(EXIT_FAILURE);
}
The primary changes are:
Closing the second end of the pipe after the call to dup2().
Revising the code in the parent branch to rework the I/O redirection.
This compiles without warnings under my preferred stringent compilation options (on a Mac running macOS Sierra 10.12.6 still, with GCC 7.2.0):
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
fork79.c -o fork79
I created a pair of minimal shell scripts to work as preC and sortC:
$ cat preC
#!/bin/sh
exec ls -t *.c
$ cat sortC
#!/bin/sh
exec sort
$
The basic output from preC is:
fork79.c pth47.c cpp13.c th89.c mnmx67.c
pl53.c pth23.c fd19.c cp53.c mm19.c
da73.c bs79.c fd53.c th59.c mm53.c
example.c pm73.c fd23.c th19.c mm23.c
rf13.c nr13.c fd97.c cp41.c mm59.c
pipe97.c cola83.c fd47.c mnmx71.c bst29.c
pipe83.c cola61.c cp79.c mergesort47.c
(When the output is piped, the file names are listed one per line.)
The output from running fork97 (created by compiling fork79.c as shown) is:
bs79.c
bst29.c
cola61.c
cola83.c
cp41.c
cp53.c
cp79.c
cpp13.c
da73.c
example.c
fd19.c
fd23.c
fd47.c
fd53.c
fd97.c
fork79.c
mergesort47.c
mm19.c
mm23.c
mm53.c
mm59.c
mnmx67.c
mnmx71.c
nr13.c
pipe83.c
pipe97.c
pl53.c
pm73.c
pth23.c
pth47.c
rf13.c
th19.c
th59.c
th89.c
This shows that the output is sorted by name from sortC.
Round 2
After discussion, it seems that it will be best if the original process forks and executes the preC process with a pipe from the standard output of the preC read from the original process. Then the original process should execute the sortC process.
To support this exercise, I created a data file data containing:
abyssinian
yosemite
sierra
high
ocelot
jaguar
tiger
lion
leopard
snow leopard
puma
panther
cougar
I created a revised program from the source fork31.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MSGSIZE 512
int main(void)
{
int p[2];
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch (fork())
{
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup2(p[1], STDOUT_FILENO); // redirect standard output and error output
close(p[0]); // close link
close(p[1]);
execl("./preC", "preC", (char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
{
close(p[1]);
int nbytes;
char inbuf[MSGSIZE];
while ((nbytes = read(p[0], inbuf, MSGSIZE)) > 0)
write(STDOUT_FILENO, inbuf, nbytes);
close(p[0]);
execl("./sortC", "sortC", (char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
}
/*NOTREACHED*/
return(EXIT_FAILURE);
}
This is close to the revised code in 'next revision' in the question. The primary difference is the use of > instead of >= to recognize EOF. With the low-level (file descriptor) I/O function read(), EOF is indicated by zero bytes read; -1 indicates an error, and EOF is not an error. I also use write() to write the data to the standard output.
I changed preC shell script to use ls -Ct *.c to reduce the amount of output.
The result of invoking was:
$ fork31 < data
fork31.c pipe83.c cola61.c cp79.c mergesort47.c
fork79.c pth47.c cpp13.c th89.c mnmx67.c
pl53.c pth23.c fd19.c cp53.c mm19.c
da73.c bs79.c fd53.c th59.c mm53.c
example.c pm73.c fd23.c th19.c mm23.c
rf13.c nr13.c fd97.c cp41.c mm59.c
pipe97.c cola83.c fd47.c mnmx71.c bst29.c
abyssinian
cougar
high
jaguar
leopard
lion
ocelot
panther
puma
sierra
snow leopard
tiger
yosemite
$
The output from the ls is no longer sorted by a secondary sort, of course. The list of names fed to sortC from data was sorted as expected. Note that if ls read from its standard input, this would not have worked — the sort process would have gotten no data to sort. Fortunately, ls is a data generator — it doesn't read its standard input.
I'm trying to write a c program that is the equivalent of the linux command ps -aux | sort -r -n -k 5 but I'm not getting any output
Here's my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char ** argv){
int pipes[2];
int r;
r = pipe(pipes);
if (r < 0) {
fprintf(stderr, "pipe failed\n\n"); // stderr is a FILE* variable for the standard error file (terminal)
exit(2);
}
int saved_stdout = dup(1);
int pid = fork();
if(pid > 0){
// Parent
pid = fork();
if(pid > 0){
// Parent
wait(NULL);
}else if (pid == 0){
// Child 1
printf("Child 1\n");
dup2(pipes[1], 1);
close(pipes[0]);
close(pipes[1]);
execlp("/bin/ps", "ps", "-aux", (char*) NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
}else if (pid == 0){
// Child 2
printf("Child 2\n");
dup2(pipes[0], 0);
close(pipes[0]);
close(pipes[1]);
dup2(saved_stdout, 1);
close(saved_stdout);
execlp("/bin/sort", "sort", "-r", "-n", "-k", "5", (char*)NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
wait(NULL);
printf("Exiting parent\n");
}
The output I get is this
Child 1
Child 2
Exiting parent
I doesn't actually print the execlp command, I've tried saving stdout to variable saved_stdout which is the solution I found in another answer, but that doesn't seem to work.
How can I redirect stdout back to the terminal?
Strange my output with your code is:
Child 1
Child 2
and the program don't stop. Or you sure that your output is valid ?
Whatever, your problem is that you don't close your pipe in your parents. Just add:
close(pipes[0]);
close(pipes[1]);
In your both parents (before your two call to wait()).
Plus saved_stdout is useless in your case, because you only change stdout in your child1. saved_stdout and 1 describe the same file in your child2.
I have to write a program that will perform the same operation that du | sort | head in the command line would do, but I'm stuck, and my program is not working. The output right now is 112 . and the program doesn't terminate. Please help, I don't know what to do!
int main(void) {
int fd[2];
int fd1[2];
int pid;
if (pipe(fd) == -1) {
perror("Pipe");
exit(1);
}
switch (fork()) {
case -1:
perror("Fork");
exit(2);
case 0:
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execl("/usr/bin/du", "du", (char *) 0);
exit(3);
}
if (pipe(fd1) == -1) {
perror("Pipe");
exit(1);
}
switch (fork()) {
case -1:
perror("Fork");
exit(2);
case 0:
dup2(fd[0], STDIN_FILENO);
dup2(fd1[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/sort", "sort", (char *) 0);
exit(3);
}
close(fd[0]);
close(fd[1]);
switch (fork()) {
case -1:
perror("Fork");
exit(2);
case 0:
dup2(fd1[0], STDIN_FILENO);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/head", "head", (char *) 0);
exit(3);
}
}
Let head be your parent process, sort — its child process, and du — the child of sort, or the grandchild of head.
You need two pipes, thus, two arrays — fd and fd1. Let the fd pipe connect sort with head, and fd1 — du with sort.
You will need one big switch statement, which will determine whether you currently are in the parent process (head, pipe(fd) is not 0) or the child (sort, pipe(fd) is 0). If you are in sort, you need to create the fd1 pipe and run the grandchild process du. Now, since you again have two processes (three in total), you need to set a pipe according to your location — whether you are in the grandchild or the child process. You can use a similar switch statement as you did for pipe fd. The trick here is to set the input and output for fd1 pipe correctly.
Your code must do something like this:
int main(void) {
int fd[2]; // sort <===> head
int fd1[2]; // du <===> sort
pipe(fd);
switch (fork()) {
case 0: // Are we in sort?
pipe(fd1); // If yes, let's make a new pipe!
switch (fork()) {
case 0: // Are we in du?
dup2(fd1[1], STDOUT_FILENO);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/du", "du", (whatever directory), NULL);
exit(1);
default:
/* If not in du, we're in sort! in the middle!
Let's set up both input and output properly.
We have to deal with both pipes */
dup2(fd1[0], STDIN_FILENO);
dup2(fd[1], STDOUT_FILENO);
close(fd1[0]);
close(fd1[1]);
execl("/usr/bin/sort", "sort (flags if needed)", (char *) 0);
exit(2);
}
exit(3);
default: // If we're not in sort, we're in head
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execl("/usr/bin/head", "head (flags if needed)", (char *) 0);
exit(4);
}
}
Can someone please explain to me why my output has duplicates in it from the ls command. The normal operation of ls -l | sort does not give me a duplicated output so what could be the issue?
Essentially i'm trying to pipe the output from one command and enter it into another command. The program works so far, but the output is displaying duplicate data. Plus and explanation of why I would need to do a close after dup2 would be really helpful :)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
// function declarations
void executeLs(int data_pipe[]);
void executeSort(int data_pipe[]);
int main(){
int data_pipe[2]; // array storing the file descriptors
int childls_pid; // ls child process
int childSort_pid; // sort child process
int rc; // return vaue of the pipe
int child_status1;
int child_status2;
rc = pipe(data_pipe);
if(rc == -1){
perror("pipe");
exit(1);
}
childls_pid = fork();
childSort_pid = fork();
// Ls Child process
switch(childls_pid) {
case -1:
perror("fork childLs Error");
exit(1);
case 0:
// inside of child process
executeLs(data_pipe);
exit(0);
default:
break;
}
// Sort child process
switch(childSort_pid) {
case -1:
perror("fork childSort Error");
exit(1);
case 0:
executeSort(data_pipe);
exit(0);
default:
wait(&child_status2);
}
return 0;
}
void executeLs(int data_pipe[]){
// Closes the read file descriptor
close(data_pipe[0]);
dup2(data_pipe[1], STDOUT_FILENO);
// confused as to why this is necessary
close(data_pipe[1]);
execlp("ls", "ls", "-1", NULL);
}
void executeSort(int data_pipe[]){
// close the write file descriptor
close(data_pipe[1]);
dup2(data_pipe[0], STDIN_FILENO);
close(data_pipe[0]);
execlp("sort","sort", NULL);
}
The reason is that you're forking more processes than you intended. When you do:
childls_pid = fork();
childSort_pid = fork();
you're doing the second fork() in both the original parent process and the process created by the first fork(). So you now have the following process tree:
parent
childls
childSort
childSort
In both childls and childls->childSort, childls_pid is 0, so they both execute the case 0: clause that runs executeLs().
You need to move the second fork into code that only runs in the original parent. You can simply move it to after the first switch statement.
The reason you need to close the pipe FDs is because a pipe isn't really closed until all processes that have it open close it. If the child process has the write end of its pipe open, that will keep it from reading EOF on the pipe.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
// function declarations
void executeLs(int data_pipe[]);
void executeSort(int data_pipe[]);
int main(){
int data_pipe[2]; // array storing the file descriptors
int childls_pid; // ls child process
int childSort_pid; // sort child process
int rc; // return vaue of the pipe
int child_status1;
int child_status2;
rc = pipe(data_pipe);
if(rc == -1){
perror("pipe");
exit(1);
}
childls_pid = fork();
// Ls Child process
switch(childls_pid) {
case -1:
perror("fork childLs Error");
exit(1);
case 0:
// inside of child process
executeLs(data_pipe);
exit(0);
default:
break;
}
childSort_pid = fork();
// Sort child process
switch(childSort_pid) {
case -1:
perror("fork childSort Error");
exit(1);
case 0:
executeSort(data_pipe);
exit(0);
default:
wait(&child_status2);
}
return 0;
}
void executeLs(int data_pipe[]){
// Closes the read file descriptor
close(data_pipe[0]);
dup2(data_pipe[1], STDOUT_FILENO);
// confused as to why this is necessary
close(data_pipe[1]);
execlp("ls", "ls", "-1", NULL);
}
void executeSort(int data_pipe[]){
// close the write file descriptor
close(data_pipe[1]);
dup2(data_pipe[0], STDIN_FILENO);
close(data_pipe[0]);
execlp("sort","sort", NULL);
}
I'm creating various processes (3 to be precise) and making them do different things.
So far so good. I'm trying to wait in the parent until all children are completed. I've played around with many options (such as the one listed below) but either the parent waits but I have to press enter to return to the shell (meaning that some child completes after the parent?) or the parent never returns to the shell. Any ideas? pointers to where to look for more help? Thanks
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define READ_END 0
#define WRITE_END 1
int main (int argc, char **argv)
{
pid_t pid;
int fd[2];
int fd2[2];
pipe(fd);
pipe(fd2);
for (int i=0; i<3; i++) {
pid=fork();
if (pid==0 && i==0) {
//never uses fd2, so close both descriptors
close(fd2[READ_END]);
close(fd2[WRITE_END]);
printf("i'm the child used for ls \n");
close(fd[READ_END]); /*close read end since I don't need it */
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
execlp("ls", "ls", "-hal", NULL);
break; /*exit for loop to end child's code */
}
else if (pid==0 && i==1) {
printf("i'm in the second child, which will be used to run grep\n");
close(fd[WRITE_END]);
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
close(fd2[READ_END]);
dup2(fd2[WRITE_END], STDOUT_FILENO);
close(fd2[WRITE_END]);
execlp("grep", "grep","p",NULL);
break;
}
else if (pid==0 && i==2) {
//never uses fd so close both descriptors
close(fd[READ_END]);
close(fd[WRITE_END]);
printf("i'm in the original process which will be replaced with wc\n");
close(fd2[WRITE_END]);
dup2(fd2[READ_END], STDIN_FILENO);
close(fd2[READ_END]);
printf("going to exec wc\n");
execlp("wc","wc","-w",NULL);
break;
}
else {
//do parenty things
}
}
wait(NULL);
while (1){
wait(NULL);
if(errno== ECHILD) {
printf("all children ended\n");
break;
}
}
close(fd[READ_END]);
close(fd[WRITE_END]);
close(fd2[READ_END]);
close(fd2[WRITE_END]);
return 0;
}
grep and wc never exit.
Why? They never receive an EOF on stdin.
Why? Because, even though ls has exited and closed the write end of pipe(fd), the main process still has the write end of pipe(fd) open, thus the read end of pipe(fd) is still waiting for more data. Similar thing goes for fd2: even if grep exited, wc wouldn't get an EOF on stdin.
Solution: close all the pipe fds in the main process before you wait.