因此,此代码在管道命令后终止:例如cat text.txt | wc
。它可以打开文本文件并对单词进行计数,但不会返回菜单,而是终止。有什么想法吗?
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include <fcntl.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#define LOGOUT 20
#define MAXNUM 40
#define MAXLEN 160
#define MAXSIZE 100
#define BUFSIZE 20
#define TRUE 1
#define FALSE 0
typedef struct command_history
{
char *command;
struct command_history *next;
} history;
/* Main */
int main(void)
{
char *currentpath, *cmd, line[MAXLEN], *args[MAXNUM], *args2[MAXNUM], *args3[MAXNUM];
int background, i, j, t, wildcard, opts;
int redir_in, redir_out;
int fd;
long size;
char *buf;
pid_t pid, pid2;
char *history[MAXSIZE], command[MAXSIZE], command_number[MAXSIZE], *temp;
int history_count = 0, length, number, offs;
int pipefd[2], pipefd2[2];
int numberofpipes, pipesuccess;
char buffer[BUFSIZE];
FILE *fp;
buf = (char*)malloc((size_t)size);
/* set the signal handler for alarm */
/*get default working directory as current path*/
currentpath = getenv("PWD");
while (1)
{
/* initialize */
background = FALSE;
opts = FALSE;
redir_in = FALSE; redir_out = FALSE;
pipesuccess = FALSE;
numberofpipes = 0, t = 0, offs = 0;
/* print the prompt */
fprintf(stdout, "%s > ", currentpath);
/* set the timeout for autologout, function alarm() */
/* read the users command */
if (fgets(line,MAXLEN,stdin) == NULL) {
fprintf(stdout, "\nlogout\n");
exit(0);
}
line[strlen(line) - 1] = '\0';
if (strlen(line) == 0)
continue;
/* start to background? (check if the last character is '&') */
if(line[strlen(line)-1] == '&')
{
line[strlen(line)-1] = '\0'; //remove '&'
background = TRUE;
}
/* saving the user command into history list */
if(line[0] != '!'){
if (history_count < MAXSIZE){
history[history_count] = strdup(line);
history_count++;
}
}
/* split the command line to args[]*/
i = 0; //number of arguments
cmd = line;
while((args[i] = strtok(cmd, " ")) != NULL)
{
i++; //argument count
cmd = NULL;
}
/* history usage */
if(line[0] == '!')
{
// find right command index from history
j = 0;
while (isdigit(line[j+1])){
command_number[j] = line[j+1];
j++;
}
number = atoi(command_number);
if (number <= history_count)
{
// parsing the history commands back into args
t = 0;
temp = history[number-1];
while((args[t] = strtok(temp, " ")) != NULL)
{
t++;
temp = NULL;
}
}
else
{
printf("Out of range of array.\n");
}
}
/* find and open redirected files*/
for(j = 0; j < i; j++)
{
if(strcmp(args[j], ">") == 0){
args[j] = NULL;
fd = open(args[j+1], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
redir_out = TRUE;
if (fd < 0) {
perror("open");
exit(0);
}
}
else if(strcmp(args[j], "<") == 0){
args[j] = NULL;
fd = open(args[j+1], O_RDONLY, S_IRUSR | S_IWUSR);
redir_in = TRUE;
if (fd < 0) {
perror("open");
exit(0);
}
}
}
/* exit? */
if (strcmp(args[0], "exit") == 0) {
exit(0);
}
/* cd */
if (strcmp(args[0], "cd") == 0)
{
if(i > 1)
{
if(chdir(args[1]) != 0) //change to inputted directory
{
perror("chdir");
}
currentpath = getcwd(buf, (size_t)size); //set currentpath to cwd
}
else
{
currentpath = getenv("HOME"); // change to homedir
}
continue;
}
/* history */
if(strcmp(args[0], "history") == 0)
{
printf("Command history: \n");
for (j = 0; j < history_count; j++) {
printf("[%d] %s \n", j+1, history[j]);
}
continue;
}
/* find pipe marks for single and double pipe*/
int k=0, z=0, y=0;
if(!redir_out && !redir_in){
for (j = 0; j < i; j++) //i == argument count
{
if(strncmp(args[j], "|", 1) == 0){
args[j] = NULL; // make it null, so exec will stop there
numberofpipes++;
continue;
}
if(numberofpipes == 1){
args2[z] = args[j]; //copy other part of args to args2
args2[z+1] = NULL; //make sure last args2 is (null)
z++;
/* continue; */
}
/* if(numberofpipes == 2){
args3[y] = args[j]; //copy other part of args to args3
args3[y+1] = NULL; //make sure last args3 is (null)
y++;
} */
}
}
/* single pipe */
if(numberofpipes == 1){
pipe(pipefd);
if((pid = fork()) == -1){
perror("fork");
exit(1);
}
if(pid == 0) { /* child, args2 */
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
execvp(args[0], args);
}
else { /* parent, args */
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
execvp(args2[0], args2);
perror("execv");
}
}
/* double pipe */
else if(numberofpipes == 2){
pipe(pipefd);
pipe(pipefd2);
if((pid = fork()) == -1){
perror("fork");
exit(1);
}
if(pid == 0) {
if((pid2 = fork()) == -1){
perror("fork 2");
exit(1);
}
if(pid2 == 0){ /* grandchild, args3 */
close(pipefd[0]);
close(pipefd[1]);
close(pipefd2[1]);
dup2(pipefd2[0], STDIN_FILENO);
close(pipefd2[0]);
execvp(args3[0], args3);
perror("execv");
}
else { /* child, args2 */
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(pipefd2[0]);
dup2(pipefd2[1], STDOUT_FILENO);
close(pipefd2[0]);
execvp(args2[0], args2);
perror("execv");
}
}
else{ /* parent, args */
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
execvp(args[0], args);
}
}
/* fork to run the command */
switch (pid = fork()) {
case -1:
/* error */
perror("fork");
continue;
case 0:/* child process, exec() */
if(redir_out){ //redirection to output file
dup2(fd, STDOUT_FILENO);
close(fd);
}
else if(redir_in){ //redirection to input file
dup2(fd, STDIN_FILENO);
close(fd);
}
execvp(args[0], args);
perror("execv");
exit(1);
default:
/* parent (shell), wait() if not background process */
if(!background){
alarm(0);
while(wait(NULL) != pid)
{
printf("please.");
}
}
break;
}
}
//globfree(&globbuf);
free(buf);
return 0;
}
答案 0 :(得分:2)
您的单管道代码块是:
/* single pipe */
if(numberofpipes == 1){
pipe(pipefd);
if((pid = fork()) == -1){
perror("fork");
exit(1);
}
if(pid == 0) { /* child, args2 */
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
execvp(args[0], args);
}
else { /* parent, args */
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
execvp(args2[0], args2);
perror("execv");
}
}
您的父进程执行管道的两个部分之一;当然,它会在管道终止时终止。
你需要再次分叉,沿线的某个地方。你可以让父进程(主shell)轮流分配每个子进程,或者你可以让父fork一次,然后第一个子进行创建管道。第一个优点是父shell可以找到管道中每个进程的退出状态 - 这就是Bash所做的。第二个的优点是它更像旧壳做的。第一个子进程执行管道中的最后一个进程,其退出状态控制整个管道的退出状态。
使用第二种方法,第一个子项创建管道,父项不必担心关闭它们。使用第一种方法,父进程必须确保在等待任何退出之前关闭管道。
我编辑它http://pastebin.com/yyMBnKPu现在它没有终止但是第二次运行cat然后挂起。
您使用哪些编译选项?我总是编译我的C代码挑剔,并最终编写SO的答案与繁琐的选项。使用来自PasteBin的代码(470多行 - 合理的不包括问题中的所有内容),我得到:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition sh61.c -o sh61
sh61.c:30:6: error: no previous prototype for ‘sighandler’ [-Werror=missing-prototypes]
void sighandler(int sig)
^~~~~~~~~~
sh61.c: In function ‘sighandler’:
sh61.c:30:21: error: unused parameter ‘sig’ [-Werror=unused-parameter]
void sighandler(int sig)
^~~
sh61.c: At top level:
sh61.c:38:6: error: no previous prototype for ‘left_child’ [-Werror=missing-prototypes]
void left_child(char *args[MAXNUM], int pipefd[2])
^~~~~~~~~~
sh61.c:49:6: error: no previous prototype for ‘right_child’ [-Werror=missing-prototypes]
void right_child(char *args2[MAXNUM],int pipefd[2])
^~~~~~~~~~~
sh61.c:59:5: error: no previous prototype for ‘SinglePipe2’ [-Werror=missing-prototypes]
int SinglePipe2(char *args[MAXNUM], char *args2[MAXNUM], int redir_in, int redir_out, int fd){
^~~~~~~~~~~
sh61.c: In function ‘SinglePipe2’:
sh61.c:115:13: error: this ‘if’ clause does not guard... [-Werror=misleading-indentation]
if (execvp(args[0], args))
^~
sh61.c:117:5: note: ...this statement, but the latter is misleadingly indented as if it is guarded by the ‘if’
exit(1);
^~~~
sh61.c: In function ‘main’:
sh61.c:367:19: error: "/*" within comment [-Werror=comment]
if(pid == 0) { /* child, args2
sh61.c:374:26: error: "/*" within comment [-Werror=comment]
else { parent, args /*
sh61.c:397:21: error: "/*" within comment [-Werror=comment]
if(pid2 == 0){ /* grandchild, args3
sh61.c:406:13: error: "/*" within comment [-Werror=comment]
else { /* child, args2
sh61.c:417:10: error: "/*" within comment [-Werror=comment]
else{ /* parent, args
sh61.c:428:5: error: "/*" within comment [-Werror=comment]
/* error
sh61.c:431:11: error: "/*" within comment [-Werror=comment]
case 0:/* child process, exec()
sh61.c:456:5: error: "/*" within comment [-Werror=comment]
/* parent (shell), wait() if not background process
sh61.c:332:7: error: unused variable ‘k’ [-Werror=unused-variable]
int k=0, z=0, y=0;
^
sh61.c:142:8: error: unused variable ‘fp’ [-Werror=unused-variable]
FILE *fp;
^~
sh61.c:141:7: error: unused variable ‘buffer’ [-Werror=unused-variable]
char buffer[BUFSIZE];
^~~~~~
sh61.c:140:21: error: variable ‘pipesuccess’ set but not used [-Werror=unused-but-set-variable]
int numberofpipes, pipesuccess;
^~~~~~~~~~~
sh61.c:139:17: error: unused variable ‘pipefd2’ [-Werror=unused-variable]
int pipefd[2], pipefd2[2];
^~~~~~~
sh61.c:139:6: error: unused variable ‘pipefd’ [-Werror=unused-variable]
int pipefd[2], pipefd2[2];
^~~~~~
sh61.c:138:25: error: unused variable ‘length’ [-Werror=unused-variable]
int history_count = 0, length, number, offs;
^~~~~~
sh61.c:137:26: error: unused variable ‘command’ [-Werror=unused-variable]
char *history[MAXSIZE], command[MAXSIZE], command_number[MAXSIZE], *temp;
^~~~~~~
sh61.c:136:19: error: unused variable ‘act’ [-Werror=unused-variable]
struct sigaction act;
^~~
sh61.c:130:6: error: variable ‘background’ set but not used [-Werror=unused-but-set-variable]
int background, i, j, t, wildcard, opts;
^~~~~~~~~~
sh61.c:129:73: error: variable ‘args3’ set but not used [-Werror=unused-but-set-variable]
char *currentpath, *cmd, line[MAXLEN], *args[MAXNUM], *args2[MAXNUM], *args3[MAXNUM];
^~~~~
cc1: all warnings being treated as errors
$
第115ff行的问题是:
if (execvp(args[0], args))
perror("execv");
exit(1);
perror()
由if
控制,但exit()
无条件发生。但是,由于execvp()
仅在失败时返回,因此无需进行测试:
execvp(args[0], args);
perror("execv");
exit(1);
未使用的变量很痛苦。严重嵌套的评论是一个不好的迹象。如果您想要消除代码块,请先使用#if 0
,然后使用#endif
。使用这些功能是个好主意。使用VCS(版本控制系统)是个好主意。使用注释来消除代码块是一个坏的想法。随着注释掉的代码消失,文件大小减少到370行。
未声明的函数警告来自我对严格原型等的坚持。除了main()
之外的所有函数都是static
(只能从它们写入的文件中访问),除非有一个声明它们的标题,标头用于定义函数的文件和使用该函数的所有文件。就个人而言,我认为这是非常有益的。
处理未使用的变量大多很简单。请注意you're not supposed to use printf()
in a signal handler。
修复这些问题后,您有一个问题,即您已将代码注释掉以将redir_in
和redir_out
设置为已知值(0
或FALSE
)所以不一定要输入管道代码。修复后,SinglePipe2()
中的代码如下所示:
close(pipefd[0]); /* close parent's copy of pipe */
close(pipefd[1]);
while ((ret = wait(&status)) > 0) /* wait for children */
{
if (ret == left_pid)
printf("left child %d terminated, status: 0x%.4X\n", ret, status);
else if (ret == right_pid)
printf("right child %d terminated, status: 0x%.4X\n", ret, status);
else
printf("yow! unknown child %d terminated, status %x\n",
ret, status);
}
#if 0
switch (pid = fork())
{
case -1:
perror("fork");
case 0: /* child process, exec() */
if (redir_out) // redirection to output file
{
dup2(fd, STDOUT_FILENO);
close(fd);
}
else if (redir_in) // redirection to input file
{
dup2(fd, STDIN_FILENO);
close(fd);
}
execvp(args[0], args);
perror("execv");
exit(1);
}
#endif /* 0 */
除非您的非工作代码没有#if 0
和#endif
行注释掉导致您看到的错误行为的代码。删除那段代码。
请注意,重定向处理代码不足。写这是合法的:
cat < sh61.c | wc -l > wc.out
在左手子进程上有输入重定向,在右手子进程上有输出重定向。但是,这是一个未来的问题。您的MCVE(Minimal, Complete, Verifiable Example)不应该包含'历史'处理和I / O重定向处理代码 - 您还不担心这些部分。可以说,cd
命令也应该被排除在外。我可以看到保留exit
代码的原因,尽管shell也可以干净地处理EOF。