bestsource

openstdin, stdout, stderr 방향 전환을 제어하는 방법?

bestsource 2023. 10. 1. 21:06
반응형

openstdin, stdout, stderr 방향 전환을 제어하는 방법?

저는 popen()이 유닉스에서 자식 프로세스의 stdin, stdout 및 stderr을 어떻게 리디렉션하는지에 대해 혼란스럽습니다.popen()의 man 페이지는 이와 관련하여 매우 명확하지 않습니다.더콜

FILE *p = popen("/usr/bin/foo", "w");

자식 프로세스를 포크하고 "-c", "/usr/bin/foo" 인수가 있는 셸을 실행하고 이 셸의 stdin(foo의 stdin으로 리디렉션됨)을 stdout 상단으로 리디렉션합니다.하지만 stderr는 어떻게 됩니까?그 이면의 일반적인 원리는 무엇입니까?

foop에서 파일을 열었을 때(fopen, socket, accept 등을 사용) 부모 프로세스에 stdout이 없으면 다음 사용 가능한 파일 번호가 1 등으로 할당되는 것을 확인했습니다.이는 fprintf(stderr, ...)와 같은 호출에서 예상치 못한 결과를 제공합니다.

글로 피할 수 있습니다.

FILE *p = popen("/usr/bin/foo 2>/dev/null", "w");

부모 프로그램에서, 하지만 그들의 더 나은 방법은?

popen(3)단지 도서관 기능일 뿐입니다.fork(2)그리고.pipe(2)진짜 일을 하기 위해서 입니다.

하지만pipe(2)단방향 파이프만 만들 수 있습니다.자식 프로세스 입력을 보내고 출력을 캡처하려면 파이프 두 개를 열어야 합니다.

만약 당신이 캡처하고 싶다면.stderr그것도 가능하지만 그럼 파이프 세 가 필요하고, 그리고 한 개가 필요합니다.select루프를 사용하여 읽기를 중재합니다.stdout그리고.stderr냇물

여기 두 개의 파이프 버전에 대한 예가 있습니다.

간단한 아이디어: 명령 문자열에 "2>&1"을 추가하여 bash를 stderr을 stdout으로 리디렉션하도록 강제하는 것은 어떨까요(좋아, stdin에 쓰는 것은 여전히 불가능하지만 적어도 우리는 stderr과 stdout을 우리의 C 프로그램에 얻습니다).

popen()의 반환 값은 fclose(3)가 아닌 pclose()로 닫아야 한다는 점을 제외하면 모든 면에서 정상 표준 I/O 스트림입니다.이러한 스트림에 기록하는 것은 명령어의 표준 입력에 기록합니다. 명령어의 표준 출력은 명령어 자체에 의해 변경되지 않는 한 popen()이라는 프로세스의 출력과 동일합니다.반대로 "opened" 스트림에서 읽게 되면 명령의 표준 출력이 읽혀지고 명령의 표준 입력은 popen()이라는 프로세스의 입력과 동일합니다.

manpage에서 명령 표준 출력을 읽거나 표준 입력에 쓸 수 있습니다.stderr에 대해서는 아무것도 나와있지 않습니다.따라서 이는 재연결되지 않습니다.

"w"를 입력하면 실행되는 셸의 stdin으로 당신의 물건을 보냅니다.그래서 하는 것은

FILE * file = popen("/bin/cat", "w");
fwrite("hello", 5, file);
pclose(file);

에서 /bin/cat 합니다 합니다."hello"표준 입력 스트림으로서.를 들어 어 stderr오로 재연결하십시오."foo"위의 코드를 실행하기 전에 먼저 이 작업을 수행합니다.

FILE * error_file = fopen("foo", "w+");
if(error_file) {
    dup2(fileno(error_file), 2);
    fclose(error_file);
}

파일을 열고 파일 설명자를 2로 복제하여 원래 파일 설명자를 닫습니다.

open1을 얻을 것입니다. 그것은 (stdin이 이미 열려 있는 경우) 다음 무료 파일 descriptor이기 때문입니다.내가 볼 수 있는 유일한 해결책은 위의 코드처럼 dup2를 사용하고 무언가를 부모에 있는 것으로 복제하는 것입니다.하십시오.stdout, 그것은 성공하지 못할 것입니다.stdout부모에게도 열려 있습니다.그곳은 닫혀있습니다.

바트 트로이아노프스키의 팝펜 RWE를 확인해보세요.3개의 파이프를 모두 깨끗하게 하는 방법.

STDERR만 얻고 싶다면 다음을 시도해 보십시오.

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

/*
 * Pointer to array allocated at run-time.
 */
static pid_t    *childpid = NULL;

/*
 * From our open_max(), {Prog openmax}.
 */
static int      maxfd;

FILE *
mypopen(const char *cmdstring, const char *type)
{
    int     i;
    int     pfd[2];
    pid_t   pid;
    FILE    *fp;

    /* only allow "r" "e" or "w" */
    if ((type[0] != 'r' && type[0] != 'w' && type[0] != 'e') || type[1] != 0) {
        errno = EINVAL;     /* required by POSIX */
        return(NULL);
    }

    if (childpid == NULL) {     /* first time through */
        /* allocate zeroed out array for child pids */
        maxfd = 256;
        if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
            return(NULL);
    }

    if (pipe(pfd) < 0)
        return(NULL);   /* errno set by pipe() */

    if ((pid = fork()) < 0) {
        return(NULL);   /* errno set by fork() */
    } else if (pid == 0) {                          /* child */
        if (*type == 'e') {
            close(pfd[0]);
            if (pfd[1] != STDERR_FILENO) {
                dup2(pfd[1], STDERR_FILENO);
                close(pfd[1]);
            }
        } else if (*type == 'r') {
            close(pfd[0]);
            if (pfd[1] != STDOUT_FILENO) {
                dup2(pfd[1], STDOUT_FILENO);
                close(pfd[1]);
            }
        } else {
            close(pfd[1]);
            if (pfd[0] != STDIN_FILENO) {
                dup2(pfd[0], STDIN_FILENO);
                close(pfd[0]);
            }
        }

        /* close all descriptors in childpid[] */
        for (i = 0; i < maxfd; i++)
            if (childpid[i] > 0)
                close(i);

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);
    }

    /* parent continues... */
    if (*type == 'e') {
        close(pfd[1]);
        if ((fp = fdopen(pfd[0], "r")) == NULL)
            return(NULL);
    } else if (*type == 'r') {
        close(pfd[1]);
        if ((fp = fdopen(pfd[0], type)) == NULL)
            return(NULL);

    } else {
        close(pfd[0]);
        if ((fp = fdopen(pfd[1], type)) == NULL)
            return(NULL);
    }

    childpid[fileno(fp)] = pid; /* remember child pid for this fd */
    return(fp);
}

int
mypclose(FILE *fp)
{
    int     fd, stat;
    pid_t   pid;

    if (childpid == NULL) {
        errno = EINVAL;
        return(-1);     /* popen() has never been called */
    }

    fd = fileno(fp);
    if ((pid = childpid[fd]) == 0) {
        errno = EINVAL;
        return(-1);     /* fp wasn't opened by popen() */
    }

    childpid[fd] = 0;
    if (fclose(fp) == EOF)
        return(-1);

    while (waitpid(pid, &stat, 0) < 0)
        if (errno != EINTR)
            return(-1); /* error other than EINTR from waitpid() */

    return(stat);   /* return child's termination status */
}

int shellcmd(char *cmd){
    FILE *fp;
    char buf[1024];
    fp = mypopen(cmd,"e");
    if (fp==NULL) return -1;

    while(fgets(buf,1024,fp)!=NULL)
    {
        printf("shellcmd:%s", buf);
    }

    pclose(fp);
    return 0;
}

int main()
{
    shellcmd("ls kangear");
}

그리고 당신은 이것을 얻게 될 것입니다.

shellcmd:ls: cannot access kangear: No such file or directory

언급URL : https://stackoverflow.com/questions/280571/how-to-control-popen-stdin-stdout-stderr-redirection

반응형