APUE 第3版第15页的shell2.c示例程序,运行效果如下:
1 gcc ol.shell.c -o origin_shell 2 3 ./origin_shell 4 % date 5 2015年12月13日 星期日 22时04分48秒 CST 6 % ^Cinterrupt 7 ls 8 Makefile getcputc.c mycat.c myls_info.c note.md origin_shell shell2.c uidgid.c 9 a.out hello.c myls.c myls_sort.c ol.shell.c shell1.c testerror.c 10 % % 11 couldn't execute: : No such file or directory 12 % ls 13 Makefile getcputc.c mycat.c myls_info.c note.md origin_shell shell2.c uidgid.c 14 a.out hello.c myls.c myls_sort.c ol.shell.c shell1.c testerror.c 15 %
从上述示例中可以看出,存在2个小问题:
1. 当通过ctrl+C输入中断信号时,中断捕获程序的输出语句的“%”并没有立即输出来,等到下一次回车时才输出,导致第10行有两个%%号。
2. 当出现%提示符时,直接回车,会提示如11行所示的错误。因为此时传递给execlp函数的buf内容是空的。其中问题1是出现在程序中的如下一句:
while (fgets(buf, MAXLINE, stdin) != NULL) { } // 即中断信号中断的是fgets函数,此时系统正在请求输入中,而中断处理程序的输出: printf("interrupt\n%% "); // 在换行之后,%并没有立即出来。如果将其修改如下,
printf("interrupt\n%%\n"); // 则显示效果变成这样:
1 % ^Cinterrupt 2 % 3 ls 4 Makefile mycat.c note.md shell2.c 5 a.out myls.c ol.shell.c testerror.c 6 getcputc.c myls_info.c origin_shell uidgid.c 7 hello.c myls_sort.c shell1.c
如上可见,上述增加一个'\n'后,虽然立即输出了%,但如第3行所见,光标移到%下一行了。
一个解决办法是使用fflush()函数。完整代码,新增为21-24行,以及46行:
1 #include "../apue.h" 2 #include <sys/wait.h> 3 4 static void sig_int(int); /* our signal-catching function */ 5 6 int 7 main(void) 8 { 9 char buf[MAXLINE]; /* from apue.h */ 10 pid_t pid; 11 int status; 12 13 if (signal(SIGINT, sig_int) == SIG_ERR) 14 err_sys("signal error"); 15 16 printf("%% "); /* print prompt (printf requires %% to print %) */ 17 while (fgets(buf, MAXLINE, stdin) != NULL) { 18 if (buf[strlen(buf) - 1] == '\n') 19 buf[strlen(buf) - 1] = 0; /* replace newline with null */ 20 21 if (strlen(buf) == 0) { // 此if语句用于处理直接回车 22 printf("%% "); 23 continue; 24 } 25 26 if ((pid = fork()) < 0) { 27 err_sys("fork error"); 28 } else if (pid == 0) { /* child */ 29 execlp(buf, buf, (char *)0); 30 err_ret("couldn't execute: %s", buf); 31 exit(127); 32 } 33 34 /* parent */ 35 if ((pid = waitpid(pid, &status, 0)) < 0) 36 err_sys("waitpid error"); 37 printf("%% "); 38 } 39 exit(0); 40 } 41 42 void 43 sig_int(int signo) 44 { 45 printf(" interrupt!\n%%"); 46 fflush(stdout); //强制输出所有stdout中的字符流 47 }
完善后效果如下:
1 % date 2 2015年12月13日 星期日 22时31分53秒 CST 3 % ^C interrupt! 4 % date 5 2015年12月13日 星期日 22时32分15秒 CST 6 % 7 %