I'm trying to execute grep -o colour colourfile.txt | wc -w > newfile.txt through a program in C, instead of using the command line.
This is what I have so far:
#include <stdlib.h>
#include <unistd.h>
int main (void) {
int fd[2];
pipe(fd);
if (fork()) {
// Child process
dup2(fd[0], 0); // wc reads from the pipe
close(fd[0]);
close(fd[1]);
execlp("wc", "wc", "-w", ">", "newfile.txt", NULL);
} else {
// Parent process
dup2(fd[1], 1); // grep writes to the pipe
close(fd[0]);
close(fd[1]);
execlp("grep", "grep", "-o", "colour", "colourfile.txt", NULL);
}
exit(EXIT_FAILURE);
}
if (fork()) { means parent process not child process, see http://man7.org/linux/man-pages/man2/fork.2.html
You should handle > like | use open()
The following code could work:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main (void) {
int pipefd[2];
pipe(pipefd);
if (fork()) {
// Child process
dup2(pipefd[0], 0); // wc reads from the pipe
close(pipefd[0]);
close(pipefd[1]);
int fd = open("newfile.txt", O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR);
dup2(fd, 1);
close(fd);
execlp("wc", "wc", "-w", NULL);
} else {
// Parent process
dup2(pipefd[1], 1); // grep writes to the pipe
close(pipefd[0]);
close(pipefd[1]);
execlp("grep", "grep", "-o", "colour", "colourfile.txt", NULL);
}
exit(EXIT_FAILURE);
}
Related
I wrote the following code in order to pipe two commands:
#include <stdlib.h>
#include <unistd.h>
char *program_1[3] = {"/bin/cat", "/dev/random", NULL};
char *program_2[2] = {"/bin/ls", NULL};
char *program_3[2] = {"/usr/bin/sort", NULL};
int main(void)
{
int fd[2];
int pid;
pipe(fd);
if ((pid = fork()) == 0) //Child process
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execve(program_3[0], program_3, NULL);
}
else if (pid > 0) //Parent process
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execve(program_2[0], program_2, NULL);
}
return (EXIT_SUCCESS);
}
Each pair of program_x / program_y where x != y works fine, except this one.
When i pipe sort into ls, ls well prints its output on stdout, but then, sort throw this error: sort: Input/output error.
When I type sort | ls into bash, it prints ls result as my program, but then waits for input.
Am I doing someting wrong ?
edit: I'm trying to reimplement the shell's behaviour
The problem is that when ls finishes, the parent process will exit which will close the read-end of the pipe, which will lead to an error being propagated to the write-end of the pipe which is detected by sort and it write the error message.
That it doesn't happen in the shell is because shells handle pipes differently than your simple example program, and it keeps the right-hand side of the pipe open and running (possibly in the background) until you pass EOF (Ctrl-D) to the sort program.
Your program isn't quite equivalent to what a shell typically does.
You're replacing the parent with ls; whereas shell would create who child processes and connect them and wait for them to finish.
It's more like:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
char *program_2[2] = {"/bin/ls", NULL};
char *program_3[2] = {"/usr/bin/sort", NULL};
int main(void)
{
int fd[2];
pid_t pid;
pid_t pid2;
pipe(fd);
if ((pid = fork()) == 0) //Child process
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execve(program_3[0], program_3, NULL);
}
else if (pid > 0) //Parent process
{
if ( (pid2 = fork()) == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execve(program_2[0], program_2, NULL);
}
}
waitpid(pid, 0, 0);
waitpid(pid2, 0, 0);
return (EXIT_SUCCESS);
}
I finally found the solution, we were close to:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
char *cat[3] = {"/bin/cat", "/dev/random", NULL};
char *ls[2] = {"/bin/ls", NULL};
char *sort[2] = {"/usr/bin/sort", NULL};
int main(void)
{
int fd[2];
pid_t pid;
pid_t pid2;
pipe(fd);
if ((pid = fork()) == 0)
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execve(cat[0], cat, NULL);
}
else if (pid > 0)
{
if ( (pid2 = fork()) == 0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execve(ls[0], ls, NULL);
}
waitpid(pid2, 0, 0);
close(fd[0]);
}
waitpid(pid, 0, 0);
return (EXIT_SUCCESS);
}
We need to close the read end of the pipe once the last process ends, this way, if the first process tries to write on the pipe, an error will be throwed and the process will exit, else if it only reads from stdin as sort, it will keep reading as stdin is still open
I'm trying to recreate pipe for a homemade shell and I can make the pipe work in this case:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int stat_loc;
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if(pid != 0)
{
waitpid(pid, &stat_loc, WUNTRACED);
close(pipefd[1]);
dup2(pipefd[0], 0);
close(pipefd[0]);
execlp("cat", "cat", "-e", NULL);
}
else
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls", "ls", NULL);
}
//do something else
printf("out to parent");
return 0;
}
I would use the functional one above but I need to keep the parent process working.
But when I add one more fork, and the process gets stuck in
execlp("cat", "cat", "-e", NULL);
This is the full attempt that gets stuck:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int stat_loc;
int pipefd[2];
pipe(pipefd);
pid_t pid1 = fork();
if (pid1 == 0)
{
pid_t pid = fork();
if(pid != 0)
{
waitpid(pid, &stat_loc, WUNTRACED);
close(pipefd[1]);
dup2(pipefd[0], 0);
close(pipefd[0]);
execlp("cat", "cat", "-e", NULL);
}
else
{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ls", "ls", NULL);
}
}
waitpid(pid1, &stat_loc, WUNTRACED);
//do something else
printf("out to parent");
return 0;
}
Thanks for the help!!
You need to close all the pipe FDs in the parent process as well. Otherwise, cat will never read EOF from its input pipe.
Add:
close(pipefd[0]);
close(pipefd[1]);
before:
waitpid(pid1, &stat_loc, WUNTRACED);
when compiling this code and calling
./prog ls ls wc 1.txt
(supposed to be
(ls; ls) | wc > 1.txt
this code stalls and done right only after Control-d. What's the matter?
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd[2];
pipe(fd);
if (!fork()) {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
if (!(fork())) {
execlp(argv[1], argv[1], NULL);
_exit(1);
}
wait(NULL);
if (!fork()) {
execlp(argv[2], argv[2], NULL);
_exit(1);
}
wait(NULL);
}
close(fd[1]);
wait(NULL);
if (!fork()) {
dup2(fd[0], 0);
close(fd[0]);
int ffd = open(argv[4], O_WRONLY | O_CREAT | O_TRUNC, 0600);
dup2(ffd, 1);
close(ffd);
execlp(argv[3], argv[3], NULL);
_exit(1);
}
close(fd[0]);
wait(NULL);
return 0;
}
You need to exit the program in the first child process, otherwise both the original process and the child execute the code at the bottom that runs wc reading from the pipe.
Or you can put all that code in an else block, so it doesn't run in the child process.
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
int fd[2];
pipe(fd);
if (!fork()) {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
if (!(fork())) {
execlp(argv[1], argv[1], NULL);
_exit(1);
}
wait(NULL);
if (!fork()) {
execlp(argv[2], argv[2], NULL);
_exit(1);
}
wait(NULL);
} else {
close(fd[1]);
wait(NULL);
if (!fork()) {
dup2(fd[0], 0);
close(fd[0]);
int ffd = open(argv[4], O_WRONLY | O_CREAT | O_TRUNC, 0600);
dup2(ffd, 1);
close(ffd);
execlp(argv[3], argv[3], NULL);
_exit(1);
}
close(fd[0]);
wait(NULL);
return 0;
}
}
I am using dup2(), pipe(), and fork() to process commands with input of another. The output from the ls is correctly passed into cat, and the terminal displays output, but it does not stop receiving input. In other words cat does not terminate, so I can continue typing.
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
int main() {
int pipefd[2], child_pid, grand_child;
pipe(pipefd);
child_pid = fork();
if (child_pid) {
waitpid(child_pid, NULL, 0);
/* Parent */
grand_child = fork();
if (!grand_child) {
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("cat", "cat", NULL);
} else {
waitpid(grand_child, NULL, 0);
}
} else {
/* Child */
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
close(pipefd[0]);
execlp("ls", "ls", NULL);
}
return 0;
}
The parent still has the write side of the pipe open. cat is waiting for the parent to close it, and the parent is waiting for cat to terminate. You should close both sides of the pipe in the parent before you wait for the grand child.
I'm trying to create two child processes and pipe them, but the second child is not sorting the output produced by the first child which does ls. What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
pipe(pipefd);
if ((ls_pid = fork()) == 0) {
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[0]);
execl("/bin/ls", "ls", 0);
perror("exec ls failed");
exit(EXIT_FAILURE);
}
if ((wc_pid = fork()) == 0) {
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[1]);
execl("/usr/bin/sort", "sort", NULL);
perror("exec wc failed");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
The sort should work, but there are 2 caveats in your code, first, make sure close fd in all the processes that holds references to the fd, otherwise the fd won't close, and that's why the sort process hangs there when done, because it does not receive the EOF from stdin, and that's because the pipefd in the parent process were not closed. The other one is make sure wait the children to exit and check their exit status. Add the following to the send of main function:
close(pipefd[0]);
close(pipefd[1]);
int status;
int pid = waitpid(ls_pid, &status, 0);
pid = waitpid(wc_pid, &status, 0);
You have to pass NULL as the third parameter of the firtexecl just like you do in the second one. What happens is that execl executes correctly (that's why you dont get an error) but the ls command does not work as you give it an invalid command.
Btw, you should make error control on all OS requests, like in fork()
Combining all comments, and tested:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
int status;
pipe(pipefd);
if ((ls_pid = fork()) == 0) {
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("ls", "ls", NULL);
perror("exec ls failed");
exit(EXIT_FAILURE);
}
if ((wc_pid = fork()) == 0) {
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("sort", "sort", NULL);
perror("exec sort failed");
exit(EXIT_FAILURE);
}
close (pipefd[0]);
close (pipefd[1]);
/* wait for two children to finish */
wait(&status);
wait(&status);
return EXIT_SUCCESS;
}