Reputation: 400
I'm trying to make ls | tr a b > text.txt
I have piping done, but I can't add STDOUT to the end of the pipe (STDOUT in my case can be only in the last argument)
I mark the part of the code, in which redirection should be done, I think that file should be opened, and dup2
method used, but I don't know in which way
Methods contains piping -
enum reqType { PIPE, STDOUT };
int spawn_proc (int in, int out, char** cmd) {
pid_t pid;
if ((pid = fork ()) == 0) {
if (in != 0) {
dup2 (in, 0);
close (in);
}
if (out != 1) {
dup2 (out, 1);
close (out);
}
return execvp (cmd[0], cmd);
}
return pid;
}
void fork_pipes (int n, char** cmd[], enum reqType type) {
int i;
pid_t pid;
int in, fd [2];
in = 0;
for (i = 0; i < n - 1; ++i) {
if(type == PIPE || i < n-2) {
pipe (fd);
spawn_proc (in, fd [1], cmd[i]);
close (fd [1]);
in = fd [0];
}
else if(type == STDOUT && i == n-2) {
///HOW TO IMPLEMENT THIS PART?
}
}
if (in != 0)
dup2 (in, 0);
execvp (cmd[i][0], cmd[i]);
}
EDIT in the marked by /// place I wrote
pipe(fd);
int out = open(cmd[n-1][0],O_WRONLY|O_CREAT|O_TRUNC);
spawn_proc(in, out, cmd[i]);
close(fd[1]);
Upvotes: 2
Views: 1881
Reputation: 24738
I think that file should be opened, and
dup2
method used, but I don't know in which way
You are right about the mechanisms for implementing the redirection. It should be done on the process intended for tr
, and before performing the overlay.
Let's go step by step:
ls | tr a b > text.txt
First create a pipe, then fork()
.
From now on, there are two processes running in parallel, both of them will be eventually overlaid by means of exec()
: one with the ls
program, the other with the tr
program.
ls
:dup2()
the writing end of the pipe to STDOUT
: what this process writes to STDOUT
is being written to the pipe.exec()
with ls
.tr
:dup2()
the reading end of the pipe to STDIN
: what this process reads from STDIN
is coming from the pipe.In order to perform the redirection to the text.txt
file, first open()
the file text.txt
for writing and with the flags O_CREAT
and O_TRUNC
, then dup2()
the obtained file descriptor to STDOUT
.
Perform the overlay: exec()
with tr
.
Note that, if the command were appending to text.txt
instead of truncating it (i.e.: using >>
instead of >
):
ls | tr a b >> text.txt
You would have to use the flag O_APPEND
instead of O_TRUNC
when open()
ing the text.txt
file.
I've modified your code (also the interface of fork_pipes()
). It's a minimal example that runs, I hope it helps.
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int spawn_proc (int in, int out, char** cmd) {
pid_t pid;
if ((pid = fork ()) == 0) {
if (in != 0) {
dup2 (in, 0);
close (in);
}
if (out != 1) {
dup2 (out, 1);
close (out);
}
return execvp (cmd[0], cmd);
}
return pid;
}
void fork_pipes (char** cmd[], const char *redirection) {
int i, n;
int in, out, fd[2];
in = 0;
// obtain n from the NULL terminated cmd array
for (n = 0; cmd[n]; ++n)
;
// process all but the last elemet of the pipe
for (i = 0; i < n-1; ++i) {
pipe(fd);
spawn_proc(in, fd[1], cmd[i]);
close(fd [1]);
in = fd [0];
}
// process the last element of the pipe
if (redirection) {
out = open(redirection, O_WRONLY | O_CREAT | O_TRUNC);
fchmod(out, 0666);
} else
out = STDOUT_FILENO;
if (in != 0)
dup2(in, 0);
spawn_proc(in, out, cmd[i]);
}
int main()
{
char *cmd1[] = {"ls", NULL};
char *cmd2[] = {"tr", "a", "b", NULL};
char **cmd[] = { cmd1, cmd2, NULL};
// redirected to text.txt
fork_pipes(cmd, "text.txt");
// no redirection
fork_pipes(cmd, NULL);
// another example with a longer pipe
{
char *cmd1[] = {"echo", "hello world", NULL};
char *cmd2[] = {"tee", NULL};
char *cmd3[] = {"tee", NULL};
char *cmd4[] = {"tr", "lo", "10", NULL};
char **cmd[] = {cmd1, cmd2, cmd3, cmd4, NULL};
// redirected to redirection.txt
fork_pipes(cmd, "redirection.txt");
// no redirected
fork_pipes(cmd, NULL);
}
return 0;
}
As already pointed out in this comment. You just need to call pipe()
once in your example: The pipe()
system call only needs to be called once for each pipe operator (i.e.: the |
character) found in the compound command. For example, in the following command:
cmd1 | cmd2 | cmd3 | cmd4
pipe()
must be called exactly four times, since there are four pipe operators.
Upvotes: 2