복붙노트

[PYTHON] 파이썬에서 stdout에 C 공유 라이브러리가 인쇄되지 않게하려면 어떻게해야합니까?

PYTHON

파이썬에서 stdout에 C 공유 라이브러리가 인쇄되지 않게하려면 어떻게해야합니까?

내가 stdout에 인쇄 C 공유 라이브러리를 가져 오는 파이썬 lib와 함께 작동합니다. 파이프와 함께 사용하거나 파일을 리디렉션하기 위해 깨끗한 출력을 원합니다. 인쇄물은 파이썬 외부, 공유 라이브러리에서 처리됩니다.

처음에는 다음과 같은 접근 방식이 사용되었습니다.

# file: test.py
import os
from ctypes import *
from tempfile import mktemp

libc = CDLL("libc.so.6")

print # That's here on purpose, otherwise hello word is always printed

tempfile = open(mktemp(),'w')
savestdout = os.dup(1)
os.close(1)
if os.dup(tempfile.fileno()) != 1:
    assert False, "couldn't redirect stdout - dup() error"

# let's pretend this is a call to my library
libc.printf("hello world\n")

os.close(1)
os.dup(savestdout)
os.close(savestdout)

이 첫 번째 접근법은 절반의 작업입니다. - 어떤 이유로 stdout을 옮기기 바로 전에 "print"문이 필요합니다. 그렇지 않으면 hello word가 항상 출력됩니다. 결과적으로 라이브러리가 일반적으로 출력하는 모서리 대신 빈 줄이 인쇄됩니다. - 더 성가신, 그것은 파일로 리디렉션 할 때 실패 :

$python test.py > foo && cat foo

hello world

내 두 번째 파이썬 시도는 주석에 제공된 다른 유사한 스레드에서 영감을 얻었다.

import os
import sys
from ctypes import *
libc = CDLL("libc.so.6")

devnull = open('/dev/null', 'w')
oldstdout = os.dup(sys.stdout.fileno())
os.dup2(devnull.fileno(), 1)

# We still pretend this is a call to my library
libc.printf("hello\n")

os.dup2(oldstdout, 1)

이 또한 "안녕하세요"가 인쇄하지 못하게합니다.

이것이 조금 낮은 수준이라고 느꼈으므로, 나는 ctypes로 완전히 가기로 결정했습니다. 나는 아무것도 인쇄하지 않는이 C 프로그램에서 영감을 얻었다.

#include <stdio.h>

int main(int argc, const char *argv[]) {
    char buf[20];
    int saved_stdout = dup(1);
    freopen("/dev/null", "w", stdout);

    printf("hello\n"); // not printed

    sprintf(buf, "/dev/fd/%d", saved_stdout);
    freopen(buf, "w", stdout);

    return 0;
}

다음 예제를 만들었습니다.

from ctypes import *
libc = CDLL("libc.so.6")

saved_stdout = libc.dup(1)
stdout = libc.fdopen(1, "w")
libc.freopen("/dev/null", "w", stdout);

libc.printf("hello\n")

libc.freopen("/dev/fd/" + str(saved_stdout), "w", stdout)

이것은 printf 바로 뒤에 libc.fflush (stdout)가 있더라도 "hello"를 출력합니다. 파이썬에서 원하는 것을 할 수 없다고 생각하기 시작했습니다. 아니면 stdout에 파일 포인터를 얻는 방법이 옳지 않다.

어떻게 생각해?

해결법

  1. ==============================

    1.@Yinon Ehrlich의 대답을 기반으로합니다. 이 변종은 파일 기술자 유출을 피하려고합니다 :

    @Yinon Ehrlich의 대답을 기반으로합니다. 이 변종은 파일 기술자 유출을 피하려고합니다 :

    import os
    import sys
    from contextlib import contextmanager
    
    @contextmanager
    def stdout_redirected(to=os.devnull):
        '''
        import os
    
        with stdout_redirected(to=filename):
            print("from Python")
            os.system("echo non-Python applications are also supported")
        '''
        fd = sys.stdout.fileno()
    
        ##### assert that Python and C stdio write using the same file descriptor
        ####assert libc.fileno(ctypes.c_void_p.in_dll(libc, "stdout")) == fd == 1
    
        def _redirect_stdout(to):
            sys.stdout.close() # + implicit flush()
            os.dup2(to.fileno(), fd) # fd writes to 'to' file
            sys.stdout = os.fdopen(fd, 'w') # Python writes to fd
    
        with os.fdopen(os.dup(fd), 'w') as old_stdout:
            with open(to, 'w') as file:
                _redirect_stdout(to=file)
            try:
                yield # allow code to be run with the redirected stdout
            finally:
                _redirect_stdout(to=old_stdout) # restore stdout.
                                                # buffering and flags such as
                                                # CLOEXEC may be different
    
  2. ==============================

    2.예, 두 번째 아이디어처럼 os.dup 대신 os.dup2를 사용하고 싶습니다. 코드가 다소 우연히 보입니다. / dev / null을 제외하고는 / dev 항목을 쓰지 마십시오. 불필요합니다. 여기 C에서 아무것도 쓰지 않아도됩니다.

    예, 두 번째 아이디어처럼 os.dup 대신 os.dup2를 사용하고 싶습니다. 코드가 다소 우연히 보입니다. / dev / null을 제외하고는 / dev 항목을 쓰지 마십시오. 불필요합니다. 여기 C에서 아무것도 쓰지 않아도됩니다.

    트릭은 dup을 사용하여 stdout fdes를 저장 한 다음 fdopen에 전달하여 새로운 sys.stdout Python 객체를 작성하는 것입니다. 한편 fdes를 / dev / null로 열고 dup2를 사용하여 기존 stdout fdes를 덮어 씁니다. 그런 다음 이전 fdes를 / dev / null로 닫습니다. dup2를 호출하는 것이 필요합니다. 왜냐하면 어떤 fdes를 반환 할지를 알 수 없기 때문에 dup2가이를 수행하는 유일한 방법입니다.

    편집 : 그리고 당신이 파일로 리디렉션하는 경우, stdout 라인 버퍼되지 않습니다, 그래서 당신은 그것을 플러시해야합니다. 파이썬에서 그렇게 할 수 있으며, C와 정확하게 상호 운용됩니다. 물론 stdout에 아무 것도 쓰지 않기 전에이 함수를 호출하면 문제가되지 않습니다.

    다음은 방금 테스트 한 예제입니다.

    import zook
    import os
    import sys
    
    def redirect_stdout():
        print "Redirecting stdout"
        sys.stdout.flush() # <--- important when redirecting to files
        newstdout = os.dup(1)
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, 1)
        os.close(devnull)
        sys.stdout = os.fdopen(newstdout, 'w')
    
    zook.myfunc()
    redirect_stdout()
    zook.myfunc()
    print "But python can still print to stdout..."
    

    "zook"모듈은 C에서 매우 간단한 라이브러리입니다.

    #include <Python.h>
    #include <stdio.h>
    
    static PyObject *
    myfunc(PyObject *self, PyObject *args)
    {
        puts("myfunc called");
        Py_INCREF(Py_None);
        return Py_None;
    }
    
    static PyMethodDef zookMethods[] = {
        {"myfunc",  myfunc, METH_VARARGS, "Print a string."},
        {NULL, NULL, 0, NULL}
    };
    
    PyMODINIT_FUNC
    initzook(void)
    {
        (void)Py_InitModule("zook", zookMethods);
    }
    

    출력은?

    $ python2.5 test.py
    myfunc called
    Redirecting stdout
    But python can still print to stdout...
    

    파일로 리디렉션합니까?

    $ python2.5 test.py > test.txt
    $ cat test.txt
    myfunc called
    Redirecting stdout
    But python can still print to stdout...
    
  3. ==============================

    3.컨텍스트 관리자에게 두 답변을 결합하여 해당 범위에 대해 stdout으로 인쇄를 차단하는 컨텍스트 관리자 (첫 번째 대답의 코드 외부 출력을 차단했는데, 후자의 대답은 sys.stdout.flush ()를 마지막에 놓쳤습니다.

    컨텍스트 관리자에게 두 답변을 결합하여 해당 범위에 대해 stdout으로 인쇄를 차단하는 컨텍스트 관리자 (첫 번째 대답의 코드 외부 출력을 차단했는데, 후자의 대답은 sys.stdout.flush ()를 마지막에 놓쳤습니다.

    class HideOutput(object):
        '''
        A context manager that block stdout for its scope, usage:
    
        with HideOutput():
            os.system('ls -l')
        '''
    
        def __init__(self, *args, **kw):
            sys.stdout.flush()
            self._origstdout = sys.stdout
            self._oldstdout_fno = os.dup(sys.stdout.fileno())
            self._devnull = os.open(os.devnull, os.O_WRONLY)
    
        def __enter__(self):
            self._newstdout = os.dup(1)
            os.dup2(self._devnull, 1)
            os.close(self._devnull)
            sys.stdout = os.fdopen(self._newstdout, 'w')
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            sys.stdout = self._origstdout
            sys.stdout.flush()
            os.dup2(self._oldstdout_fno, 1)
    
  4. ==============================

    4.내가 마침내 한 일이 여기있다. 나는 이것이 다른 사람들에게 유용 할 수 있기를 바란다. (이것은 나의 리눅스 스테이션에서 작동한다.)

    내가 마침내 한 일이 여기있다. 나는 이것이 다른 사람들에게 유용 할 수 있기를 바란다. (이것은 나의 리눅스 스테이션에서 작동한다.)

    나는 자랑스럽게 외부 라이브러리를 닥치기 위해 고안된 libshutup을 제시한다.

    1) 다음 파일을 복사하십시오.

    // file: shutup.c
    #include <stdio.h>
    #include <unistd.h>
    
    static char buf[20];
    static int saved_stdout;
    
    void stdout_off() {
        saved_stdout = dup(1);
        freopen("/dev/null", "w", stdout);
    }
    
    void stdout_on() {
        sprintf(buf, "/dev/fd/%d", saved_stdout);
        freopen(buf, "w", stdout);
    }
    

    2) 공유 라이브러리로 컴파일

    gcc -Wall -shared shutup.c -fPIC -o libshutup.so
    

    3) 이것을 다음과 같이 코드에서 사용하십시오.

    from ctypes import *
    shutup = CDLL("libshutup.so")
    
    shutup.stdout_off()
    
    # Let's pretend this printf comes from the external lib
    libc = CDLL("libc.so.6")
    libc.printf("hello\n")
    
    shutup.stdout_on()
    
  5. ==============================

    5.파이썬에서 할 때와 똑같이 할 수 없습니까? sys를 가져오고 sys.stdout 및 sys.stderr을 기본 sys.stdout 및 sys.stderr가 아닌 다른 것으로 지정합니다. 나는 라이브러리에서 출력을 버려야하는 몇 가지 응용 프로그램에서 항상이 작업을 수행합니다.

    파이썬에서 할 때와 똑같이 할 수 없습니까? sys를 가져오고 sys.stdout 및 sys.stderr을 기본 sys.stdout 및 sys.stderr가 아닌 다른 것으로 지정합니다. 나는 라이브러리에서 출력을 버려야하는 몇 가지 응용 프로그램에서 항상이 작업을 수행합니다.

  6. from https://stackoverflow.com/questions/5081657/how-do-i-prevent-a-c-shared-library-to-print-on-stdout-in-python by cc-by-sa and MIT license