I'm having a lot of troubles with an assignment for school. I'm supposed to add pipes and I/O redirection to a shell implementation in c. I already got it to work with I/O redirections and the pipes work by themselves but I need to support something like this "sort < file.txt | grep main | cat > output". I have no idea how to make it work with both at the same time. Any help would be gratly appreciated.
int get_args(char* cmdline, char* args[]){
int i = 0;
/* if no args */
if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
return 0;
while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
if(i >= MAX_ARGS) {
printf("Too many arguments!\n");
exit(1);
}
}
/* the last one is always NULL */
return i;
}
void pipehandler(char* args[], int nargs){
int num_cmds = 0;
char *command[256];
int filedes[2]; // pos. 0 output, pos. 1 input of the pipe
int filedes2[2];
int i = 0;
//calculate the number of commands
for(int i = 0; i < nargs; i++){
if (strcmp(args[i], "|") == 0){
num_cmds++;
}
}
num_cmds++;
if(num_cmds <= 1){
return;
}
int j = 0;
int end = 0;
pid_t pid;
while(args[j] != NULL && end != 1){
int k = 0;
while (strcmp(args[j],"|") != 0){
command[k] = args[j];
j++;
if (args[j] == NULL){
// 'end' variable used to keep the program from entering
// again in the loop when no more arguments are found
end = 1;
k++;
break;
}
k++;
}
// Last position of the command will be NULL to indicate that
// it is its end when we pass it to the exec function
command[k] = NULL;
j++;
// Depending on whether we are in an iteration or another, we
// will set different descriptors for the pipes inputs and
// output. This way, a pipe will be shared between each two
// iterations, enabling us to connect the inputs and outputs of
// the two different commands.
if (i % 2 != 0){
pipe(filedes); // for odd i
}else{
pipe(filedes2); // for even i
}
pid=fork();
if(pid==-1){
if (i != num_cmds - 1){
if (i % 2 != 0){
close(filedes[1]); // for odd i
}else{
close(filedes2[1]); // for even i
}
}
printf("Child process could not be created\n");
return;
}
if(pid==0){
// If we are in the first command
if (i == 0){
dup2(filedes2[1], STDOUT_FILENO);
}
// If we are in the last command, depending on whether it
// is placed in an odd or even position, we will replace
// the standard input for one pipe or another. The standard
// output will be untouched because we want to see the
// output in the terminal
else if (i == num_cmds - 1){
if (num_cmds % 2 != 0){ // for odd number of commands
dup2(filedes[0],STDIN_FILENO);
}else{ // for even number of commands
dup2(filedes2[0],STDIN_FILENO);
}
// If we are in a command that is in the middle, we will
// have to use two pipes, one for input and another for
// output. The position is also important in order to choose
// which file descriptor corresponds to each input/output
}else{ // for odd i
if (i % 2 != 0){
dup2(filedes2[0],STDIN_FILENO);
dup2(filedes[1],STDOUT_FILENO);
}else{ // for even i
dup2(filedes[0],STDIN_FILENO);
dup2(filedes2[1],STDOUT_FILENO);
}
}
execvp(command[0],command);
}
// CLOSING DESCRIPTORS ON PARENT
if (i == 0){
close(filedes2[1]);
}
else if (i == num_cmds - 1){
if (num_cmds % 2 != 0){
close(filedes[0]);
}else{
close(filedes2[0]);
}
}else{
if (i % 2 != 0){
close(filedes2[0]);
close(filedes[1]);
}else{
close(filedes[0]);
close(filedes2[1]);
}
}
waitpid(pid,NULL,0);
i++;
}
}
int searchIO(char* args[], int *nargs, char** input_file, char** output_file, int* in, int* out, int* ap){
int ioRedirection = -1;
//search through the array args
printf("searching for io redirections\n");
for(int i = 0; i < *nargs; i++){
if(strcmp(args[i], "<") == 0){
//if you find < then you need to redirect the stdin to be from a file
//if you are here then the input file is in args[i+1]
*input_file = args[i+1];
printf("this is the input file %s \n", *input_file);
//we need to remove the < and input_file from args[]
int j;
for(j = i; j < *nargs; j++){
//move what is in args[j+2] to args[j];
args[j] = args[j+2];
}
//then we need to execute the comand
*nargs = *nargs-2;
*in = 1;
i = 0;
ioRedirection = 1;
}else if (strcmp(args[i], ">") == 0){
//if you are here then the output file is in args[i+1]
//so aparently execute what is in args[0] to args[i]
*output_file = args[i+1];
//we need to remove the > and output_file from args[]
int j;
for(j = i; j < *nargs; j++){
//move what is in args[j+2] to args[j];
if(j < *nargs){
args[j] = args[j+2];
}
}
*nargs = *nargs-2;
*out = 1;
i = 0;
ioRedirection = 1;
}else if (strcmp(args[i], ">>") == 0){
//if you are here then the output file is in args[i+1]
//so aparently execute what is in args[0] to args[i]
*output_file = args[i+1];
//we need to remove the > and output_file from args[]
int j;
for(j = i; j < *nargs; j++){
//move what is in args[j+2] to args[j];
args[j] = args[j+2];
}
*nargs = *nargs-2;
*out = 1;
*ap = 1;
i = 0;
ioRedirection = 1;
}
}
return -1;
}
void execute(char* cmdline){
int pid, async;
char* args[MAX_ARGS];
char *input_file;
char *output_file;
int _in, _out, _ap;
_in = 0;
_out = 0;
_ap = 0;
int nargs = get_args(cmdline, args);
pipehandler(args, nargs);
searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap);
if(nargs <= 0) return;
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
exit(0);
}
if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; }
else async = 0;
pid = fork();
if(pid == 0) { /* child process */
if(_in == 1){
int input = open(input_file, O_RDONLY);
dup2(input, STDIN_FILENO);
close(input);
}
if(_out == 1){
int output;
if(_ap){
output = open(output_file, O_APPEND | O_CREAT);
}else{
output = open(output_file, O_CREAT | O_WRONLY, 0666);
}
dup2(output, STDOUT_FILENO);
close(output);
}
execvp(args[0], args);
/* return only when exec fails */
perror("exec failed");
exit(-1);
} else if(pid > 0) { /* parent process */
if(!async) waitpid(pid, NULL, 0);
else printf("this is an async call\n");
} else { /* error occurred */
perror("fork failed");
exit(1);
}
}
int main (int argc, char* argv [])
{
char cmdline[BUFSIZ];
for(;;) {
printf("$ ");
if(fgets(cmdline, BUFSIZ, stdin) == NULL) {
perror("fgets failed");
exit(1);
}
execute(cmdline) ;
}
return 0;
}
答案 0 :(得分:0)
如果正在使用管道,则唯一可以使用I / O重定向的地方是 第一个命令和最后一个命令但不在中间右边?
只有重定向 stdin / stdout 时才会这样。
I / O重定向和管道自己工作,但......我不知道 如何同时使用它们。
重组您的实施。如果你打电话
,它就无法工作 pipehandler(args, nargs);
searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap);
在彼此之后,这也导致错误,即管道的第一个命令被执行两次(首先来自pipehandler
,然后是来自searchIO
的错误参数)。相反,您可以让main
从pipehandler
内调用execute
然后pipehandler
每个单独的命令(即使只有一个也没有管道)。可以很容易地诊断出冲突的I / O重定向和管道。