将 child 的输出写入 shell 和文件 error.log

Write the output of the child in shell and in file error.log

我尝试将 child 的输出通过管道传输到一个文件中,然后由父亲写入。

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

int prepare_log(void){
    int fd = open("error.log",
        O_CREAT | O_RDWR | O_APPEND,
        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
    return fd;
}

void log_stderr(int pipe_ends[], int outfile){
    close(pipe_ends[1]);
    dup2(pipe_ends[0],outfile);
    close(pipe_ends[0]);

}
void child(int pipe_ends[], char* argv[]){
    close(pipe_ends[0]);
    dup2(pipe_ends[1],1);
    close(pipe_ends[1]);
    execvp(argv[0], argv);
}

void bury(){
    int status;
    wait(0);

}

int main(){
    int fd = prepare_log();
    char* argv[] = {"seq", "10", NULL};

    int pipe1[2];
    if(pipe(pipe1) == -1){
        printf("Dont' create Pipe\n");
        exit(EXIT_FAILURE);
    }


    pid_t pid = fork();
    if(pid < 0){
        perror("ERROR");
        exit(EXIT_FAILURE);
    } else if(pid > 0){
        log_stderr(pipe1, fd);
        bury();
    } else if (pid == 0){
        child(pipe1,argv);
    }
}

目前我只尝试将 child 的输出通过管道传递给父亲,然后将其写入文件。 我的最终目标也是将其显示到终端。我的想法是使用 3 个管道并重定向我们在代码中可以看到的第一个管道,作为第二个和第三个管道的输入。然后 使用 dup2(pipe2[1],file1) 重定向第二个管道的输出,使用 dup2(pipe2[1], stdout).

重定向第三个管道的输出

程序设计

parent 进程将不得不从 child 读取响应,并安排将该信息写入两次,一次写入日志文件,一次写入终端。或者您将不得不安排 child 的输出转到像 tee 这样的程序,该程序将其输入的副本写入多个目的地。

除非您使用一个管道将 child 的输出重定向到 tee

,否则更多的管道将无济于事

正在关闭管道文件描述符

您在 child 进程中没有关闭足够的文件描述符。


经验法则:如果你 dup2() 管道的一端连接到标准输入或标准输出,同时关闭 返回的原始文件描述符 pipe() 尽早。 特别是,您应该在使用任何 exec*() 函数族。

如果您使用以下任一方式复制描述符,则该规则也适用 dup() 或者 fcntl() F_DUPFDF_DUPFD_CLOEXEC.


如果 parent 进程不会通过以下方式与它的任何 children 通信 管道,它必须确保尽早关闭管道的两端 足够(例如,在等待之前)以便其 children 可以接收 EOF 指示读取(或获取 SIGPIPE 信号或写入错误 写),而不是无限期地阻塞。 即使 parent 使用管道而不使用 dup2(),它也应该 通常至少关闭管道的一端——这种情况极为罕见 在单个管道的两端读写的程序。

请注意 O_CLOEXEC 选项 open(), 并且 fcntl()FD_CLOEXECF_DUPFD_CLOEXEC 选项也可以考虑因素 进入这个讨论。

如果你使用 posix_spawn() 及其广泛的支持功能系列(总共 21 个功能), 您将需要查看如何在生成的进程中关闭文件描述符 (posix_spawn_file_actions_addclose(), 等)。

请注意,使用 dup2(a, b) 比使用 close(b); dup(a); 更安全 由于各种原因。 一个是如果你想强制文件描述符大于 通常的数字,dup2() 是唯一明智的方法。 另一个是如果 ab 相同(例如两者都是 0),则 dup2() 正确处理它(它在复制 a 之前不会关闭 b) 而单独的 close()dup() 则失败得很厉害。 这种情况不太可能,但并非不可能。

足够固定的代码

这是您程序的固定版本。 log_stderr() 中的逻辑很奇怪;它没有做的一件事是写入标准错误。我已经修改了它,所以它确实从管道读取并写入标准输出和日志文件。请注意,复制信息需要一次读取,两次写入。错误检查不足 non-existent。 bury() 函数现在报告 child 死亡。代码保存在pipe71.c中,编译为pipe71.

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static
int prepare_log(void)
{
    int fd = open("error.log",
                  O_CREAT | O_RDWR | O_APPEND,
                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH );
    return fd;
}

static
void log_stderr(int pipe_ends[], int outfile)
{
    char buffer[512];
    int nbytes;
    close(pipe_ends[1]);
    while ((nbytes = read(pipe_ends[0], buffer, sizeof(buffer))) > 0)
    {
        write(STDOUT_FILENO, buffer, nbytes);
        write(outfile, buffer, nbytes);
    }
    close(pipe_ends[0]);
    close(outfile);
}

static
void child(int pipe_ends[], char *argv[])
{
    dup2(pipe_ends[1], 1);
    close(pipe_ends[0]);
    close(pipe_ends[1]);
    execvp(argv[0], argv);
    fprintf(stderr, "failed to execute %s\n", argv[0]);
    exit(EXIT_FAILURE);
}

static
void bury(void)
{
    int status;
    int corpse;
    while ((corpse = wait(&status)) > 0)
        printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
}

int main(void)
{
    int fd = prepare_log();
    char *argv[] = {"seq", "10", NULL};

    int pipe1[2];
    pipe(pipe1);

    pid_t pid = fork();

    if (pid > 0)
    {
        log_stderr(pipe1, fd);
        bury();
    }
    else if (pid == 0)
    {
        child(pipe1, argv);
    }
    return 0;
}

但是,它确实有效:

$ rm error.log
$ ./pipe71
1
2
3
4
5
6
7
8
9
10
99989: child 99990 exited with status 0x0000
$ cat error.log
1
2
3
4
5
6
7
8
9
10
$