【问题标题】:C putchar doesn't work after stdout is redirected重定向标准输出后 C putchar 不起作用
【发布时间】:2018-04-24 21:32:20
【问题描述】:

我正在尝试实现 shell 重定向,使用它我将 stdout 重定向到 fd1

int redirectOut(int fd1)
{
    fflush(stdout);
    int fd2 = dup(STDOUT_FILENO);
    dup2(fd1, STDOUT_FILENO);
    close(fd1);
    return fd2;
}

然后我 fork 并调用一个可执行文件,它可以工作,除非可执行文件使用 putchar。

在 putchar 手册页上写着它使用标准输出。

putchar(c);等价于 putc(c,stdout)。

为什么 putchar 既不在标准输出中也不在我将流重定向到的文件中写入任何位置?

我尝试将 putchar 更改为 putc 但没有帮助,这可能与 stdout 的事实有关,如果*FILESTDOUT_FILENO 一个 int

我怎样才能使我的代码工作,为什么它可以与使用 (code for printf) 的 printf 一起工作

done = vfprintf (stdout, format, arg);

编辑更多代码

int executeBlocs(execBloc *bloc,int fileIn,int fileOut){
    if(bloc->first != NULL){
      if (strcmp(bloc->ope, ">") == 0){
         int out = open(bloc->command[0], O_WRONLY | O_CREAT | O_TRUNC , 0644);
         int returnCode = executeBlocs(bloc->first, STDIN_FILENO, out);
         redirectOut(fileOut);
         redirectIn(fileIn);
         return returnCode;
      }
    }
    else{
        redirectIn(fileIn);
        redirectOut(fileOut);
        return call(bloc->nbWords, bloc->command);
    }
}

execBloc 是一个结构,它包含执行(或文件名)操作符(>>、|、> ...)的命令和对包含命令其余部分的另一个块的引用。

如果用户输入 cat /tmp/testCat > /tmp/testCatRedirection

然后将创建包含运算符 > 和命令 /tmp/testCatRedirection 的第一个结构,第一个结构是对包含命令 cat /tmp/testCat

int call(int argc, char const *argv[]) {
  if (argc > 0){
      if (executeProgram(argv) == 1) return 1;

      if (executeStandardLibrary(argc, argv) == 1) return 1;

      if (executeDynamicLibrary(argc, argv) == 1) return 1;
  }
  return -1;
}


int executeProgram(char const *argv[]){
    //Creation de la chaine de caractère /home/kerdam/cbin/nonExecutable
    char *path = strdup(binFolder);
    strcat(path, argv[0]);


    //Test si le fichier existe et est executable
    if (access(path, F_OK|X_OK) != -1){
    //Le fichier existe et on peut l'éxecuter
    int pid = fork();

    // Error
    if (pid == -1){
      return -1;
    }
    //Fils
    else if (pid == 0) {
        // Executer la commande
        execv(path, argv);
        return 1;
    }

    // Parent process
    else {
        // Wait for child process to finish
        int childStatus;
        waitpid(pid, &childStatus, 0);
        return 1;
    }
  }
  else return -1;
}

最后是我要执行的程序的代码

#include<stdio.h>
#include<string.h>
#define MAX_FILE_NAME_CHARS 255
int main(int argc, char *argv[])
{
    FILE *fp;
    char file_name[MAX_FILE_NAME_CHARS], ch;
    int i;

     /*
      * after creating a.out, rename it as mycat for our own cat command
      * and it usage is same as standard cat command
      */
    if(argc<=1){
        printf("Utiliser cat avec aumoin un argument (un fichier) <nomfichier> \n");
        return 0;
    }

     /*
      * This is for multiple file in argument
      */
    for(i=1; i<=argc;i++){
        strncpy(file_name, argv[i], MAX_FILE_NAME_CHARS);

        fp=fopen(file_name, "r");
        if(fp == NULL) {
           printf("%s: No such file or directory\n", file_name);
           return 0;
        }

        /*
         * read file and feed contents to STDIO
         */
        while((ch=fgetc(fp)) != EOF || ch == '}'){
            putchar(ch);
        }
        fclose(fp);
    }
    return 0;
}

备注

我不应该更改我正在尝试执行的可执行文件的代码,因为我的 shell 的用户应该能够执行他们的程序,而不受他们可以使用的功能的限制。

【问题讨论】:

  • 您在哪个平台上进行实验? putc(c, stdout) 切换文件后是否按预期工作? fputc(c, stdout) 呢?从表面上看,您只是在更改文件描述符 0 引用的内容;标准 I/O 代码应继续工作,写入文件描述符 0。您确定 fd1 已打开以供写入吗? (我知道这听起来很傻,但有必要检查一下,因为我们没有可以使用的 MCVE (minimal reproducible example)。)
  • 您检查了fileno(stdout) 的值吗?但是由于乔纳森的要求,我们需要一个minimal reproducible example,我回答这个问题太快了。
  • 您确定要输出带有putchar() 调用的换行符吗?是否只是在更改文件描述符以引用文件时将其恢复(更改)为完全缓冲的输出,因此在刷新之前看不到输出?如果是行缓冲,换行可能就足够了;如果它是完全缓冲的,您可能需要使用fflush()。但是,您说 printf() 等人的工作,这表明这不是问题。
  • 首先这里有一个固定的测试程序,rextester.com/QSD96627,其次,你还是没有提供minimal reproducible example。第三,int out = open(bloc-&gt;command[0], O_WRONLY | O_CREAT | O_TRUNC , 0644); 是可疑的,你不想要int out = open(bloc-&gt;command[1], O_WRONLY | O_CREAT | O_TRUNC , 0644); 吗?
  • 请记住,fflush(stdout) 将任何挂起的输出写入 o/s,而不是将其保存在标准 I/O 缓冲区中。即使末尾没有换行符,它也会这样做。这使您有最大机会看到潜伏在stdout 缓冲区中的输出。因此,fflush(stdout) 绝不应该造成任何损害(除非您将减速时间计算为足够长的时间以确保缓冲区是空的)并且可以帮助您了解发生了什么。

标签: c shell stdout putchar


【解决方案1】:

我遇到了同样的问题并找到了解决方案。

查看读取文件 argv[1] 并将其内容写入文件 argv[2] 的代码,没有空格和换行符。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, const char* argv[]) {
  if (argc != 3) {
    printf ("Expected exactly two arguments\n");
    return 1;
  }

  int rd = open(argv[1], O_RDONLY);
  if (rd == -1) {
    printf ("Failed to open file %s\n", argv[1]);
    return 1;
  }

  int wd = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);
  if (wd == -1) {
    printf ("Failed to open file %s\n", argv[2]);
    return 1;
  }

  int temp_in = dup(0);
  int temp_out = dup(1);

  dup2(rd, 0);
  dup2(wd, 1);

  close(rd);
  close(wd);

  int written_bytes = 0;

  int c;
  while ((c = getchar()) != EOF) {
    if (c == ' ' || c == '\n') {
      continue;
    }

    putchar(c);
    written_bytes++;
  }

  // The instruction below is critically important, because
  // putchar by default writes char to an internal buffer,
  // so we have to send it to the file descriptor manually
  fflush(stdout);

  dup2(temp_in, 0);
  dup2(temp_out, 1);

  printf ("%d bytes have been written\n", written_bytes);
  return 0;
}

【讨论】:

  • 我是否必须阅读和比较 100 行代码才能了解您的不同之处? OP 的核心问题是什么
  • @Peter-ReinstateMonica 只有 50 行代码,而且非常简单。最有价值的行是fflush(stdout); 并在其上方发表评论。阅读整堆代码让你完全理解一个问题和解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-29
  • 2013-05-03
相关资源
最近更新 更多