所以这段代码在管道命令之后终止:例如 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;
}
最佳答案
通过 1
您的单管道代码块是:
/* 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");
}
}
您的父进程执行管道的两个部分之一;当然,当管道终止时它也会终止。
你需要在沿线的某个地方再次 fork 。您可以让父进程(主 shell)依次 fork 每个子进程,也可以让父进程 fork 一次,然后第一个子进程开始创建管道。第一个的优点是父 shell 可以找到管道中每个进程的退出状态 - 这就是 Bash 所做的。第二种的优点是它更像旧的 shell 所做的。第一个子进程执行管道中的最后一个进程,其退出状态控制整个管道的退出状态。
使用第二种方法,第一个子级创建管道,父级不必担心关闭它们。使用第一种方法,父进程必须确保在等待任何内容退出之前关闭管道。
<小时/>通过 2
I edited it like so http://pastebin.com/yyMBnKPu Now it doesn't terminate but runs cat a second time and then hangs.
您使用什么编译选项?我总是很挑剔地编译我的 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 行左右。
未声明的函数警告来 self 对严格原型(prototype)等的坚持。除main()
之外的所有函数都是静态
(只能从它们编写的文件中访问)除非有一个 header 声明它们,并且该 header 在定义该函数的文件以及使用该函数的所有文件中使用。就我个人而言,我认为这是非常有益的。
处理未使用的变量大多很简单。请注意 you're not supposed to use printf()
in a signal handler 。
解决这些问题后,您已经注释掉了将 redir_in
和 redir_out
设置为已知值 (0
code> 或 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
行注释掉导致您看到的错误行为的代码。删除该代码块。
future 的问题
请注意,您的重定向处理代码不充分。这样写是合法的:
cat < sh61.c | wc -l > wc.out
它在左侧子进程上具有输入重定向,在右侧子进程上具有输出重定向。然而,这是一个 future 的问题。您的 MCVE ( Minimal, Complete, Verifiable Example ) 不应该真正包含“历史”处理和 I/O 重定向处理代码 — 您还不担心这些部分。可以说,cd
命令也应该被省略。我可以看到保留 exit
代码的原因,尽管 shell 也干净地处理 EOF。
关于c - 我的 shell 突然退出循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42034242/