问题
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]);
回答1:
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.
Process for ls
:
- Close the reading end of the pipe: this process will only write to the pipe.
dup2()
the writing end of the pipe toSTDOUT
: what this process writes toSTDOUT
is being written to the pipe.- Perform the overlay:
exec()
withls
.
Process for tr
:
- Close the writing end of the pipe: this process will only read from the pipe.
dup2()
the reading end of the pipe toSTDIN
: what this process reads fromSTDIN
is coming from the pipe.In order to perform the redirection to the
text.txt
file, firstopen()
the filetext.txt
for writing and with the flagsO_CREAT
andO_TRUNC
, thendup2()
the obtained file descriptor toSTDOUT
.Perform the overlay:
exec()
withtr
.
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.
Code Snippet
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.
来源:https://stackoverflow.com/questions/47478392/redirection-at-the-end-of-the-pipe-c-shell