使用CTRL+;基于Linux的Windows子系统开发

使用CTRL+;基于Linux的Windows子系统开发,c,windows,windows-subsystem-for-linux,C,Windows,Windows Subsystem For Linux,我正在使用Windows Linux子系统完成一项作业。下面是用于为此任务编写迷你shell的C代码。 我在使用WSL时遇到了一个有趣的问题。在第35行,您可以看到我调用read函数来读取缓冲区中的内容,它会检查null。在使用WSL的同时按下Ctrl+D,它将进入if语句并无限次地打印第36行上的打印消息,直到我使用Ctrl+C退出时才会停止。当在Linux机器上运行此程序时,它会正常运行并打印一次,并将我们带到循环的顶端。 你知道这个bug是什么吗 #include <stdio.h&

我正在使用Windows Linux子系统完成一项作业。下面是用于为此任务编写迷你shell的C代码。 我在使用WSL时遇到了一个有趣的问题。在第35行,您可以看到我调用read函数来读取缓冲区中的内容,它会检查null。在使用WSL的同时按下Ctrl+D,它将进入if语句并无限次地打印第36行上的打印消息,直到我使用Ctrl+C退出时才会停止。当在Linux机器上运行此程序时,它会正常运行并打印一次,并将我们带到循环的顶端。
你知道这个bug是什么吗

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
#include <sys/types
#include <unistd.h>
#include <error.h>

char prompt[] = "$ ";

static int
Fork()
{
  pid_t pid;

  if ((pid = fork()) < 0)
    error(EXIT_FAILURE, errno, "fork error");
    return(pid);
}

int    
main(void)
{
  long MAX = sysconf(_SC_LINE_MAX);
  char buf[MAX];
  pid_t pid;
  int status, n;

  do {
    write(STDOUT_FILENO, prompt, strlen(prompt));
    fflush(NULL);
    memset(buf, 0, MAX);
    if((n = read(STDIN_FILENO, buf, MAX)) == 0) {
      printf("use exit to exit shell\n");
      continue;
    }
    buf[strlen(buf) - 1] = '\0'; // chomp '\n'

    if (strncmp(buf, "exit", MAX) == 0) { // match
      break;
    }
    pid = Fork();
    if (pid == 0) {  // child
      execlp(buf, buf, (char *)NULL);
      error(EXIT_FAILURE, errno, "exec failure");
    }
    // parent
    if ((pid = waitpid(pid, &status, 0)) < 0)
      error(EXIT_FAILURE, errno, "waitpid error");
  } while(1);
  exit(EXIT_SUCCESS);
}
#包括
#包括
#包括
#包括
#包括
#包括

#include不同的操作系统对EOF使用不同的击键。

不同的操作系统对EOF使用不同的击键。

关于read()的文档(Linux manpages v 3.54)没有指定文件结尾(ctrl/D)导致read返回除0以外的任何内容。相反,它说返回值零表示文件结束。所以你依赖于未定义的行为。 Linux上的ctrl/D会导致错误,因此read()返回-1。在本例中,您的程序退出循环。或者,按字面读取ctrl/D,然后read()返回1。

关于read()的文档(Linux手册页V3.54)没有指定文件结尾(ctrl/D)导致read返回除0以外的任何内容。相反,它说返回值零表示文件结束。所以你依赖于未定义的行为。
Linux上的ctrl/D会导致错误,因此read()返回-1。在本例中,您的程序退出循环。或者,按字面顺序读取ctrl/D,然后read()返回1。

您的程序尝试读取文件的末尾。我觉得那不是个好主意。显然,Windows同意了这一点,只是继续在以下读取时返回文件结尾。OT:about:
buf[strlen(buf)-1]='\0';//chomp'\n'
这不是删除尾随换行符的可靠方法。更好的方法是:
buf[strcspn(buf,“\n”)]='\0'
OT:about:static int Fork(){`将函数命名为相同的a C库函数是一种非常糟糕的编程实践(即使大小写不完全相同)。这可能会让读者感到非常困惑您可能期望的read()被ctrl/D打断,返回-1而不是0。这似乎是依赖于系统的行为。我有一个真正的Ubuntu,它也返回0。即使read()会返回-1,下面的代码也是可疑的。如果strlen(buf)==0会怎么样?那么
buf[strlen(buf)-1]
会怎么样?@eryksun我给你的标准规范链接说“如果设置了ICANON,则在处理时应丢弃EOF字符。”。如果WSL未能做到这一点,则必须使用
TCIFLUSH
手动丢弃EOF字符。使用
TCSET*
来回更改tty模式也可能会产生副作用。但这两种情况都不是必需的(在任何Unix系统上都不是这样)。您的程序尝试读取超过文件结尾的内容。我觉得这不是一个好主意。显然,Windows同意并一直在以下读取中返回文件结尾。OT:about:
buf[strlen(buf)-1]='\0';//chomp'\n'
这不是删除尾随换行符的可靠方法。更好的方法是:
buf[strcspn(buf,“\n”)]='\0';
OT:about:static int Fork(){`将函数命名为相同的a C库函数是一种非常糟糕的编程实践(即使大小写不完全相同)这可能会让读者非常困惑。你可能希望被ctrl/D中断的read()返回-1而不是0。这似乎是系统相关行为。我有一个真正的Ubuntu,它也返回0。即使read()返回-1,下面的代码也是可疑的。如果strlen(buf)==0怎么办?然后
buf[strlen(buf)-1]
行吗?@eryksun我给你的标准规范链接说“如果设置了ICANON,处理时将丢弃EOF字符。“。如果WSL无法做到这一点,则必须使用
TCIFLUSH
手动放弃EOF字符。使用
TCSET*
来回更改tty模式也可能会导致副作用。但这两种情况都不是必需的(在任何Unix系统上都不是)。