我正在实现我自己的 shell,并且我已经设法让 I/O 重定向与管道一起工作。但是,我无法理解我应该如何重定向 stderr,以便我可以将 >&
和 >>&
功能也合并到我的代码中。
此外,实现|&
的逻辑是否遵循?
这是我的代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void execute(char **, int, char **);
void handle_signal(int);
int parse(char *, char **, char **, int *);
void chop(char *);
#define INPUT_STRING_SIZE 80
#define NORMAL 00
#define OUTPUT_REDIRECTION 11
#define INPUT_REDIRECTION 22
#define PIPELINE 33
#define BACKGROUND 44
#define OUTPUT_APP 55
typedef void (*sighandler_t)(int);
int main(int argc, char *argv[])
{
int i, mode = NORMAL, cmdArgc;
size_t len = INPUT_STRING_SIZE;
char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
inputString = (char*)malloc(sizeof(char)*INPUT_STRING_SIZE);
char curDir[100];
while(1)
{
mode = NORMAL;
getcwd(curDir, 100);
printf("%s@%s->", getlogin(),curDir);
getline( &inputString, &len, stdin);
if(strcmp(inputString, "exit\n") == 0)
exit(0);
cmdArgc = parse(inputString, cmdArgv, &supplement, &mode);
if(strcmp(*cmdArgv, "cd") == 0)
{
chdir(cmdArgv[1]);
}
else
execute(cmdArgv, mode, &supplement);
}
return 0;
}
int parse(char *inputString, char *cmdArgv[], char **supplementPtr, int *modePtr)
{
int cmdArgc = 0, terminate = 0;
char *srcPtr = inputString;
//printf("parse fun%sends", inputString);
while(*srcPtr != '\0' && terminate == 0)
{
*cmdArgv = srcPtr;
cmdArgc++;
//printf("parse fun2%sends", *cmdArgv);
while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\0' && *srcPtr != '\n' && terminate == 0)
{
switch(*srcPtr)
{
case '&':
*modePtr = BACKGROUND;
break;
case '>':
*modePtr = OUTPUT_REDIRECTION;
*cmdArgv = '\0';
srcPtr++;
if(*srcPtr == '>')
{
*modePtr = OUTPUT_APP;
srcPtr++;
}
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
chop(*supplementPtr);
terminate = 1;
break;
case '<':
*modePtr = INPUT_REDIRECTION;
*cmdArgv = '\0';
srcPtr++;
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
chop(*supplementPtr);
terminate = 1;
break;
case '|':
*modePtr = PIPELINE;
*cmdArgv = '\0';
srcPtr++;
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
//chop(*supplementPtr);
terminate = 1;
break;
}
srcPtr++;
}
while((*srcPtr == ' ' || *srcPtr == '\t' || *srcPtr == '\n') && terminate == 0)
{
*srcPtr = '\0';
srcPtr++;
}
cmdArgv++;
}
/*srcPtr++;
*srcPtr = '\0';
destPtr--;*/
*cmdArgv = '\0';
return cmdArgc;
}
void chop(char *srcPtr)
{
while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\n')
{
srcPtr++;
}
*srcPtr = '\0';
}
void execute(char **cmdArgv, int mode, char **supplementPtr)
{
pid_t pid, pid2;
FILE *fp;
int mode2 = NORMAL, cmdArgc, status1, status2;
char *cmdArgv2[INPUT_STRING_SIZE], *supplement2 = NULL;
int myPipe[2];
if(mode == PIPELINE)
{
if(pipe(myPipe)) //create pipe
{
fprintf(stderr, "Pipe failed!");
exit(-1);
}
parse(*supplementPtr, cmdArgv2, &supplement2, &mode2);
}
pid = fork();
if( pid < 0)
{
printf("Error occured");
exit(-1);
}
else if(pid == 0)
{
switch(mode)
{
case OUTPUT_REDIRECTION:
fp = fopen(*supplementPtr, "w+");
dup2(fileno(fp), 1);
break;
case OUTPUT_APP:
fp = fopen(*supplementPtr, "a");
dup2(fileno(fp), 1);
break;
case INPUT_REDIRECTION:
fp = fopen(*supplementPtr, "r");
dup2(fileno(fp), 0);
break;
case PIPELINE:
close(myPipe[0]); //close input of pipe
dup2(myPipe[1], fileno(stdout));
close(myPipe[1]);
break;
}
execvp(*cmdArgv, cmdArgv);
}
else
{
if(mode == BACKGROUND)
;
else if(mode == PIPELINE)
{
waitpid(pid, &status1, 0); //wait for process 1 to finish
pid2 = fork();
if(pid2 < 0)
{
printf("error in forking");
exit(-1);
}
else if(pid2 == 0)
{
close(myPipe[1]); //close output to pipe
dup2(myPipe[0], fileno(stdin));
close(myPipe[0]);
execvp(*cmdArgv2, cmdArgv2);
}
else
{
;//wait(NULL);
//waitpid(pid, &status1, 0);
//waitpid(pid2, &status2, 0);
close(myPipe[0]);
close(myPipe[1]);
}
}
else
waitpid(pid, &status1, 0);
//wait(NULL);
}
}
任何帮助将不胜感激!
好的,根据 Jonathan Leffler 的建议,我修改了我的代码以包含重定向。现在我想看看我是否可以通过尝试让用户输入“>&”而不是“&>”并仍然实现“>&”的功能来稍微改变它。 “&>>”和“&|”也是如此。
但是,当我尝试发出命令 echo hello >& a.txt 时,我得到一个名为“&”的文件,该文件现在包含字符串 hello!我不确定这里出了什么问题。 @JonathanLeffler - 你能看一看并提出我可能做错了什么吗?这是更新后的代码:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
void execute(char **, int, char **);
void handle_signal(int);
int parse(char *, char **, char **, int *);
void chop(char *);
#define INPUT_STRING_SIZE 80
#define NORMAL 00
#define OUTPUT_REDIRECTION 11
#define INPUT_REDIRECTION 22
#define PIPELINE 33
#define BACKGROUND 44
#define OUTPUT_APP 55
#define OUTPUT_REDIRECTION_WITH_ERROR 66
#define OUTPUT_REDIRECTION_WITH_APPEND_ERROR 77
#define PIPELINE_WITH_ERROR 88
typedef void (*sighandler_t)(int);
int main(int argc, char *argv[])
{
int i, mode = NORMAL, cmdArgc;
size_t len = INPUT_STRING_SIZE;
char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
inputString = (char*)malloc(sizeof(char)*INPUT_STRING_SIZE);
char curDir[100];
while(1)
{
mode = NORMAL;
getcwd(curDir, 100);
printf("%s@%s->", getlogin(),curDir);
getline( &inputString, &len, stdin);
if(strcmp(inputString, "exit\n") == 0)
exit(0);
cmdArgc = parse(inputString, cmdArgv, &supplement, &mode);
if(strcmp(*cmdArgv, "cd") == 0)
{
chdir(cmdArgv[1]);
}
else
execute(cmdArgv, mode, &supplement);
}
return 0;
}
int parse(char *inputString, char *cmdArgv[], char **supplementPtr, int *modePtr)
{
int cmdArgc = 0, terminate = 0;
char *srcPtr = inputString;
//printf("parse fun%sends", inputString);
while(*srcPtr != '\0' && terminate == 0)
{
*cmdArgv = srcPtr;
cmdArgc++;
//printf("parse fun2%sends", *cmdArgv);
while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\0' && *srcPtr != '\n' && terminate == 0)
{
switch(*srcPtr)
{
/* case '&':
*modePtr = BACKGROUND;
break; */
case '>':
*modePtr = OUTPUT_REDIRECTION;
*cmdArgv = '\0';
srcPtr++;
if(*srcPtr == '>')
{
*modePtr = OUTPUT_APP;
srcPtr++;
}
else if(*srcPtr == '>&')
{
*modePtr = OUTPUT_REDIRECTION_WITH_ERROR;
srcPtr++;
}
else if(*srcPtr == '>>&')
{
*modePtr = OUTPUT_REDIRECTION_WITH_APPEND_ERROR;
srcPtr++;
}
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
chop(*supplementPtr);
terminate = 1;
break;
case '<':
*modePtr = INPUT_REDIRECTION;
*cmdArgv = '\0';
srcPtr++;
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
chop(*supplementPtr);
terminate = 1;
break;
case '|':
*modePtr = PIPELINE;
*cmdArgv = '\0';
srcPtr++;
if(*srcPtr == '|')
{
*modePtr = PIPELINE;
srcPtr++;
}
else if(*srcPtr == '|&')
{
*modePtr = PIPELINE_WITH_ERROR;
srcPtr++;
}
while(*srcPtr == ' ' || *srcPtr == '\t')
srcPtr++;
*supplementPtr = srcPtr;
//chop(*supplementPtr);
terminate = 1;
break;
}
srcPtr++;
}
while((*srcPtr == ' ' || *srcPtr == '\t' || *srcPtr == '\n') && terminate == 0)
{
*srcPtr = '\0';
srcPtr++;
}
cmdArgv++;
}
/*srcPtr++;
*srcPtr = '\0';
destPtr--;*/
*cmdArgv = '\0';
return cmdArgc;
}
void chop(char *srcPtr)
{
while(*srcPtr != ' ' && *srcPtr != '\t' && *srcPtr != '\n')
{
srcPtr++;
}
*srcPtr = '\0';
}
void execute(char **cmdArgv, int mode, char **supplementPtr)
{
pid_t pid, pid2;
FILE *fp;
int mode2 = NORMAL, cmdArgc, status1, status2;
char *cmdArgv2[INPUT_STRING_SIZE], *supplement2 = NULL;
int myPipe[2];
if(mode == PIPELINE)
{
if(pipe(myPipe)) //create pipe
{
fprintf(stderr, "Pipe failed!");
exit(-1);
}
parse(*supplementPtr, cmdArgv2, &supplement2, &mode2);
}
pid = fork();
if( pid < 0)
{
printf("Error occured");
exit(-1);
}
else if(pid == 0)
{
switch(mode)
{
case OUTPUT_REDIRECTION:
fp = fopen(*supplementPtr, "w+");
dup2(fileno(fp), 1);
break;
case OUTPUT_REDIRECTION_WITH_ERROR:
fp = fopen(*supplementPtr, "w+");
dup2(fileno(fp), 1);
dup2(2, 1);
break;
case OUTPUT_REDIRECTION_WITH_APPEND_ERROR:
fp = fopen(*supplementPtr, "a");
dup2(fileno(fp), 1);
dup2(2, 1);
break;
case OUTPUT_APP:
fp = fopen(*supplementPtr, "a");
dup2(fileno(fp), 1);
break;
case INPUT_REDIRECTION:
fp = fopen(*supplementPtr, "r");
dup2(fileno(fp), 0);
break;
case PIPELINE:
close(myPipe[0]); //close input of pipe
dup2(myPipe[1], fileno(stdout));
close(myPipe[1]);
break;
case PIPELINE_WITH_ERROR:
close(myPipe[0]);
dup2(myPipe[1], 1);
dup2(2, 1);
close(myPipe[1]);
break;
}
execvp(*cmdArgv, cmdArgv);
}
else
{
if(mode == BACKGROUND)
;
else if(mode == PIPELINE)
{
waitpid(pid, &status1, 0); //wait for process 1 to finish
pid2 = fork();
if(pid2 < 0)
{
printf("error in forking");
exit(-1);
}
else if(pid2 == 0)
{
close(myPipe[1]); //close output to pipe
dup2(myPipe[0], fileno(stdin));
close(myPipe[0]);
execvp(*cmdArgv2, cmdArgv2);
}
else
{
;//wait(NULL);
//waitpid(pid, &status1, 0);
//waitpid(pid2, &status2, 0);
close(myPipe[0]);
close(myPipe[1]);
}
}
else
waitpid(pid, &status1, 0);
//wait(NULL);
}
}
最佳答案
对于所有 I/O 重定向,操作都是对文件描述符的直接操作。关键功能是 dup2()
.
>&2
notation 将标准输出(文件描述符,fd,1)重定向到标准错误(fd = 2):
dup2(2, 1);
这使得现有的打开文件描述符 2 和(不一定打开的)文件描述符 1 引用相同的描述符。 (我在这个答案的第一版中得到了论点;也在下一次调用 dup2()
时,但最后两个是正确的。)
&>
notation 将标准错误重定向到与标准输出相同的位置:
dup2(1, 2);
对于管道 ( |&
),您首先要执行以下操作:
pipe(pair);
…fork()…
…in the writer…
dup2(pair[1], 1);
dup2(1, 2);
close(pair[0]);
close(pair[1]);
随着 >>
符号,您以追加模式打开文件,然后使用 dup2()
.
Can these be done using
dup()
instead ofdup2()
? and if so, how would it have to be done?
是的,就像您可以使用 Peano Arithmetic 计算数学一样。这很难,为什么要这么麻烦?
区别在于dup()
将描述符复制到最低的可用描述符。所以,假设标准输入(0)和标准输出(1)都打开,你可以模拟:
dup2(1, 2);
与:
close(2);
dup(1);
只要您只处理不超过 5 个的描述符,这是可以管理的。然而,dup2()
更容易使用;即使关闭的描述符少于目标描述符,它也会复制到指定的描述符。
修复更新的代码
Can you please take a look at my updated question above and suggest what I might be doing incorrectly?
收到这样的请求时,我做的第一件事就是在我常用的编译器警告标志集下编译它(从问题中逐字复制),在这种情况下,警告(通过 -Werror
选项转换为错误) ) 既丰富又严肃。如果您没有编译并看到此类警告,那么您的生活就会比需要的更艰难!
$ gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror pipes-22585525.c -o pipes-22585525
pipes-22585525.c: In function ‘main’:
pipes-22585525.c:45:9: error: implicit declaration of function ‘strcmp’ [-Werror=implicit-function-declaration]
if(strcmp(inputString, "exit\n") == 0)
^
pipes-22585525.c:34:11: error: unused variable ‘cpt’ [-Werror=unused-variable]
char *cpt, *inputString, *cmdArgv[INPUT_STRING_SIZE], *supplement = NULL;
^
pipes-22585525.c:32:27: error: variable ‘cmdArgc’ set but not used [-Werror=unused-but-set-variable]
int i, mode = NORMAL, cmdArgc;
^
pipes-22585525.c:32:9: error: unused variable ‘i’ [-Werror=unused-variable]
int i, mode = NORMAL, cmdArgc;
^
pipes-22585525.c:30:14: error: unused parameter ‘argc’ [-Werror=unused-parameter]
int main(int argc, char *argv[])
^
pipes-22585525.c:30:26: error: unused parameter ‘argv’ [-Werror=unused-parameter]
int main(int argc, char *argv[])
^
pipes-22585525.c: In function ‘parse’:
pipes-22585525.c:84:40: error: multi-character character constant [-Werror=multichar]
else if(*srcPtr == '>&')
^
pipes-22585525.c:84:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
else if(*srcPtr == '>&')
^
pipes-22585525.c:89:40: error: multi-character character constant [-Werror=multichar]
else if(*srcPtr == '>>&')
^
pipes-22585525.c:89:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
else if(*srcPtr == '>>&')
^
pipes-22585525.c:119:40: error: multi-character character constant [-Werror=multichar]
else if(*srcPtr == '|&')
^
pipes-22585525.c:119:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
else if(*srcPtr == '|&')
^
pipes-22585525.c: In function ‘execute’:
pipes-22585525.c:160:43: error: unused variable ‘status2’ [-Werror=unused-variable]
int mode2 = NORMAL, cmdArgc, status1, status2;
^
pipes-22585525.c:160:25: error: unused variable ‘cmdArgc’ [-Werror=unused-variable]
int mode2 = NORMAL, cmdArgc, status1, status2;
^
cc1: all warnings being treated as errors
$
关于argc
的警告和 argv
可以通过使用 int main(void)
来避免直到你真正解析传递给你的 shell 的参数。 strcmp()
的警告通过包含 <string.h>
来修复.其他未使用的变量警告也应得到修复。它们是普通的问题,但应该被修复,这样代码编译时不会出现警告。
另一组警告的例子是:
pipes-22585525.c: In function ‘parse’:
pipes-22585525.c:84:40: error: multi-character character constant [-Werror=multichar]
else if(*srcPtr == '>&')
^
pipes-22585525.c:84:21: error: comparison is always false due to limited range of data type [-Werror=type-limits]
变量srcPtr
是 char *
;它一次只能容纳一个字符。多字符常量是允许的,但具有实现定义的值。您唯一可以确定的是,单个字符永远不会包含一个值 '>&'
,这是第二条消息告诉您的内容。第一条消息暗示你应该写:
else if (strncmp(srcPtr, ">&", 2) == 0)
有一个警告。假设符号是 "<<"
;在 Bash 中,还有一个符号 "<<<"
.测试 "<<<"
至关重要在测试 "<<"
之前因为否则你永远不会看到较长的符号,因为较短的总是匹配。重定向也一样;您需要小心确保没有任何早期测试会排除检测后面的符号之一。
解决这些问题,您可能会顺利解决问题。如果仍然卡住,请再次 Ping。 (哦,我已经看到程序在第一次接受我严格的编译选项时会产生大量警告——这不是糟糕的代码。但是由于编译器可以告诉你你做错了什么,你是在浪费时间(可以说是我的,但这对你的 future 有帮助)不让编译器告诉你哪里出了问题。记住,它比你更了解 C!)
我将这些编译器选项或随之而来的次要变体与基本上我所有的 C 代码一起使用。它的大部分也可以在 C++ 编译器下正确编译和运行,但那是我选择穿的一件毛衫,你不必穿。
关于c - 如何在shell程序中实现&>和&>>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22585525/