Reputation: 231
I'm trying to create pipes in the shell to redirect standard streams and I am stuck now.
When I try to run this code:
int fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if (pid == 0)
{
// child process
// redirect standard input and output
dup2(fd[1], STDOUT_FILENO);
dup2(fd[0], STDIN_FILENO);
// close them (they are now redirected)
close(fd[0]);
close(fd[1]);
char *input_argv[] = {"/bin/ls", "/bin/ls", ">", "out.txt", NULL};
execv(input_argv[0], input_argv);
}
else if (pid > 0)
{
// parent process
waitpid(pid, NULL,0);
}
I got this error messages:
/bin/ls: cannot access >: No such file or directory
/bin/ls: cannot access out.txt: No such file or directory
I have no idea what they mean, what cause them and how to fix them.
What am I doing wrong?
Upvotes: 1
Views: 395
Reputation:
All in all, the code doesn't make any sense. I think the best answer one can give here is to explain the most problematic parts:
// redirect standard input and output
dup2(fd[1], STDOUT_FILENO);
dup2(fd[0], STDIN_FILENO);
fd[0]
is the reading end, fd[1]
the writing end of a pipe. Whatever you write to fd[1]
is available for read on fd[0]
. So, this is just "short-circuiting" your stdio streams, something not usefull at all.
What you want to do with pipes normally is have one pipe per direction of communication between parent and child process (e.g. child should read from parent: dup2()
the reading end to STDIN_FILENO
in the child and write to the writing end from the parent).
char *input_argv[] = {"/bin/ls", "/bin/ls", ">", "out.txt", NULL};
Now this doesn't make sense either. A >
tells a shell to open a file for writing and exec()
the child with a redirected STDOUT_FILENO
already in place. It's certainly not an argument understood by ls
here. You don't have a shell, you just exec()
ls
directly.
If your original intention was to mimic what the shell would do when given
ls > out.txt
you should just open the file out.txt
for writing and in the child code dup2()
the file descriptor of your opened file to STDOUT_FILENO
before exec()
ing ls
. There's no need for a pipe in this scenario.
edit in case you want to understand what a shell does internally for ls > out.txt
:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
/* open file for writing */
int outfd = open("/tmp/out.txt", O_CREAT|O_WRONLY, 00666);
if (outfd < 0)
{
perror("open()");
return EXIT_FAILURE;
}
/* fork child */
int pid = fork();
if (pid < 0)
{
perror("fork()");
return EXIT_FAILURE;
}
if (pid == 0)
{
/* in the child, redirect stdout to our file */
if (dup2(outfd, STDOUT_FILENO) < 0)
{
perror("dup2()");
return EXIT_FAILURE;
}
close(outfd);
/* then execute 'ls' */
execlp("ls", "ls", 0);
/* only reached when execlp() fails: */
perror("execlp()");
return EXIT_FAILURE;
}
/* we don't need the output file in the parent process: */
close(outfd);
/* wait for child to complete */
int childrc;
waitpid(pid, &childrc, 0);
/* return exit code of child process */
return childrc;
}
Of course, the code of the actual shell looks different (doesn't have names hardcoded, uses execv*
family of functions because it doesn't know the number of arguments in advance, and so on.)
Upvotes: 2