我开始在UNIX中编写shell来练习API调用,例如fork()dup2(),read()和wait()。目前,我的shell打开并运行正常。当我键入要运行的命令(例如ls -a)时,它会正确解析此命令并执行它。问题是,主循环提前终止,在单个命令后退出shell。我需要循环继续运行,直到从stdin读取'exit'。这是我目前的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <unistd.h>
static const char prompt[] = "myshell> ";
static const char sep[] = " \t\n\r";
int main()
{
int ac; // arg count
char *av[10]; //argument vector
int tty = open("/dev/tty", O_RDWR); // open tty for read/write
int pid; // process id
int status; // child process exit status
int w;
void (*istat)(int), (*qstat)(int);
if (tty == -1)
{
fprintf(stderr, "can't open /dev/tty\n");
exit(EXIT_FAILURE);
}
while (1)
{
char *arg, line[256]; // buffer to hold line of input
int i;
// prompt and read
write(tty, prompt, sizeof(prompt) - 1);
i = read(tty, line, sizeof(line));
if (i <= 0)
break;
line[i] = '\0';
// tokenize the line into av[]
ac = 0;
for (arg = strtok(line, sep); arg && ac < 10; arg = strtok(NULL, sep))
av[ac++] = arg;
if (ac > 0 && strcmp(av[0], "exit") == 0)
break;
if ((pid = fork()) == 0)
{
// this is the forked child process that is a copy of the running program
dup2(tty, 0); // stdin from tty
dup2(tty, 1); // stdout to tty
dup2(tty, 2); // stderr to tty
close(tty);
// last argument must be NULL for execvp()
av[ac] = NULL;
// execute program av[0] with arguments av[0]... replacing this program
execvp(av[0], av);
fprintf(stderr, "can't execute %s\n", av[0]);
exit(EXIT_FAILURE);
}
close(tty);
// disable interrupt (^C and kill -TERM) and kill -QUIT
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
// wait until forked child process terminated, get its exit status
while ((w = wait(&status)) != pid && w != -1)
continue;
if (w == -1)
status = -1;
}
// restore interrupt and quit signals
signal(SIGINT, istat);
signal(SIGQUIT, qstat);
exit(EXIT_SUCCESS);
}
我尝试在退出成功之上移动这些线(因此它们在循环之外和主要内部)
close(tty);
// disable interrupt (^C and kill -TERM) and kill -QUIT
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
// wait until forked child process terminated, get its exit status
while ((w = wait(&status)) != pid && w != -1)
continue;
if (w == -1)
status = -1;
}
// restore interrupt and quit signals
signal(SIGINT, istat);
signal(SIGQUIT, qstat);
exit(EXIT_SUCCESS);
}
这样做是正确循环shell,但是在执行命令的输出的最后一行上方打印提示,因为程序在打印提示并等待输入之前没有等待子进程结束。我尝试移动一些线路,但每次执行一个命令后它最终都会终止shell
答案 0 :(得分:2)
您不想在主循环中调用close(tty)
。这样做会导致下一个read(tty,...
失败,退出shell。
此外,如果你真的想要禁用SIGQUIT / SIGQUIT,你应该在循环中对称地恢复它们。
答案 1 :(得分:0)
以下发布的代码:
现在,建议的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
// added next statement for 'wait()' and 'waitpid()'
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
//#include <signal.h>
#include <unistd.h>
static const char prompt[] = "myshell> ";
static const char sep[] = " \t\n\r";
int main( void )
{
int ac; // arg count
char *av[10]; //argument vector
pid_t pid; // process id
int status; // child process exit status
int tty = open("/dev/tty", O_RDWR); // open tty for read/write
if (tty == -1)
{
perror( "open for /dev/tty failed");
exit(EXIT_FAILURE);
}
while (1)
{
char *arg;
char line[256]; // buffer to hold line of input
// prompt and read
write(tty, prompt, sizeof(prompt) - 1);
ssize_t i = read(tty, line, sizeof(line));
if (i == 0)
break;
if (i < 0)
{
perror( "read failed" );
exit( EXIT_FAILURE );
}
line[i] = '\0';
// tokenize the line into av[]
ac = 0;
for (arg = strtok(line, sep); arg && ac < 10; arg = strtok(NULL, sep))
av[ac++] = arg;
// last argument must be NULL for execvp()
av[ac] = NULL
if (ac > 0 && strcmp( av[0], "exit" ) == 0)
break;
pid = fork();
switch( pid )
{
case -1:
perror( "fork failed" );
exit( EXIT_FAILURE );
break;
case 0: // child process
// this is the forked child process that is a copy of the running program
dup2(tty, 0); // stdin from tty
dup2(tty, 1); // stdout to tty
dup2(tty, 2); // stderr to tty
close(tty);
;
// execute program av[0] with arguments av[0]... replacing this program
execvp(av[0], av);
perror( "execvp failed" );
exit(EXIT_FAILURE);
break;
default: // parent process
// wait until forked child process terminated, get its exit status
waitpid( pid, &status, 0 );
break;
}
}
return 0;
}