2016-04-30 7 views
1

Ich möchte die gesamte Terminalausgabe unterdrücken, die von einer Funktion erzeugt wird, die ausführbare Dateien ausführt.Wie kann die Terminalausgabe von ausführbaren Dateien, die von Python-Funktionen ausgeführt werden, allgemein stummgeschaltet werden?

Ich habe versucht, die Ausgabe einer Python-Funktion zu unterdrücken, indem Sie einen Kontextmanager verwenden, der stdout und stderr bei jedem Aufruf der Funktion temporär neu definiert. Dies unterdrückt die durch print Aufrufe in der Funktion erzeugte Terminalausgabe, aber es scheint nicht zu funktionieren, wenn die Funktion ausführbare Dateien aufruft, die Terminalausgabe erzeugen.

Also, wie könnte die Ausgabe von ausführbaren Dateien, die von Python-Funktionen aufgerufen werden, unterdrückt werden?

Mein Code ist unten. Ich habe eine Beispielfunktion eingefügt, die ls aufruft, um zu versuchen, die Art der Terminalausgabe zu veranschaulichen, die ich unterdrücken möchte (obwohl die Funktion, mit der ich es zu tun habe, anders ist).

#!/usr/bin/env python 

import os 
import subprocess 
import sys 

def main(): 

    print("hello") 

    with silence(): 
     print("there") 

    print("world") 

    with silence(): 
     engage_command(command = "ls") 

class silence(object): 

    def __init__(
     self, 
     stdout = None, 
     stderr = None 
     ): 
     if stdout == None and stderr == None: 
      devnull = open(os.devnull, "w") 
      stdout = devnull 
      stderr = devnull 
     self._stdout = stdout or sys.stdout 
     self._stderr = stderr or sys.stderr 

    def __enter__(
     self 
     ): 
     self.old_stdout = sys.stdout 
     self.old_stderr = sys.stderr 
     self.old_stdout.flush() 
     self.old_stderr.flush() 
     sys.stdout = self._stdout 
     sys.stderr = self._stderr 

    def __exit__(
     self, 
     exc_type, 
     exc_value, 
     traceback 
     ): 
     self._stdout.flush() 
     self._stderr.flush() 
     sys.stdout = self.old_stdout 
     sys.stderr = self.old_stderr 

def engage_command(
    command = None 
    ): 
    process = subprocess.Popen(
     [command], 
     shell  = True, 
     executable = "/bin/bash") 
    process.wait() 
    output, errors = process.communicate() 
    return output 

if __name__ == "__main__": 
    main() 

In meinem Fall, ich versuche, die folgende Funktion (statt der ls Funktion oben) ausgeführt wird:

with propyte.silence(): 
    stream = pyaudio.PyAudio().open(
     format = pyaudio.PyAudio().get_format_from_width(1), 
     channels = 1, 
     rate  = bitrate, 
     output = True 
    ) 

Wenn er gestartet wird, dies erzeugt eine Ausgabe wie folgt aus:

ALSA lib pcm_dsnoop.c:606:(snd_pcm_dsnoop_open) unable to open slave 
ALSA lib pcm_dmix.c:1029:(snd_pcm_dmix_open) unable to open slave 
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear 
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe 
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side 
ALSA lib pcm_dmix.c:1029:(snd_pcm_dmix_open) unable to open slave 
Cannot connect to server socket err = No such file or directory 
Cannot connect to server request channel 
jack server is not running or cannot be started 
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock 
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock 

Ich möchte diese Ausgabe unterdrücken.


EDIT: Testen a solution von @Matthias bereitgestellt

#!/usr/bin/env python 

import contextlib 
import os 
import subprocess 
import sys 

def main(): 

    print("hello") 

    with silence(): 
     print("there") 

    print("world") 

    with silence(): 
     engage_command(command = "ls") 

@contextlib.contextmanager 
def silence(): 
    devnull = os.open(os.devnull, os.O_WRONLY) 
    old_stderr = os.dup(2) 
    sys.stderr.flush() 
    os.dup2(devnull, 2) 
    os.close(devnull) 
    try: 
     yield 
    finally: 
     os.dup2(old_stderr, 2) 
     os.close(old_stderr) 

def engage_command(
    command = None 
    ): 
    process = subprocess.Popen(
     [command], 
     shell  = True, 
     executable = "/bin/bash") 
    process.wait() 
    output, errors = process.communicate() 
    return output 

if __name__ == "__main__": 
    main() 

Ich habe nicht gelungen, die Terminal-Ausgabe von den print bei der Unterdrückung oder die ls und ich bin mir nicht sicher, warum.

Antwort

1

Sie können von PyAudio auf das Modul sounddevice umschalten, das bereits für die Stummschaltung des Terminalausgangs sorgt (siehe #12). Dies ist, wie es ist getan (CFFI verwenden):

from cffi import FFI 
import os 

ffi = FFI() 
ffi.cdef(""" 
/* from stdio.h */ 
FILE* fopen(const char* path, const char* mode); 
int fclose(FILE* fp); 
FILE* stderr; /* GNU C library */ 
FILE* __stderrp; /* Mac OS X */ 
""") 
try: 
    stdio = ffi.dlopen(None) 
    devnull = stdio.fopen(os.devnull.encode(), b'w') 
except OSError: 
    return 
try: 
    stdio.stderr = devnull 
except KeyError: 
    try: 
     stdio.__stderrp = devnull 
    except KeyError: 
     stdio.fclose(devnull) 

Wenn Sie eine reine Python-Lösung wollen, können Sie diesen Kontext-Manager versuchen:

import contextlib 
import os 
import sys 

@contextlib.contextmanager 
def ignore_stderr(): 
    devnull = os.open(os.devnull, os.O_WRONLY) 
    old_stderr = os.dup(2) 
    sys.stderr.flush() 
    os.dup2(devnull, 2) 
    os.close(devnull) 
    try: 
     yield 
    finally: 
     os.dup2(old_stderr, 2) 
     os.close(old_stderr) 

Dies ist eine sehr hilfreiche Blog-Post über die Thema: http://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/.


UPDATE:

Der Kontext-Manager oben zum Schweigen bringt die Standard-Fehlerausgabe (stderr), die in der ursprünglichen Frage erwähnten für die lästigen Nachrichten von Portaudio verwendet wird. Um loszuwerden, die Standardausgabe (stdout), wie sie in Ihrer aktualisierten Frage, müssen Sie ersetzen sys.stderr mit sys.stdout und dem Dateideskriptor 2 mit der Nummer 1:

@contextlib.contextmanager 
def ignore_stdout(): 
    devnull = os.open(os.devnull, os.O_WRONLY) 
    old_stdout = os.dup(1) 
    sys.stdout.flush() 
    os.dup2(devnull, 1) 
    os.close(devnull) 
    try: 
     yield 
    finally: 
     os.dup2(old_stdout, 1) 
     os.close(old_stdout) 
+0

Vielen Dank für Ihre ausführliche Lösung. Ich werde das Sounddevice-Modul wie vorgeschlagen untersuchen und werde seine Lösung zum Stummschalten von PortAudio untersuchen. Für den reinen, allgemeinen Ansatz, das Terminal vollständig zum Schweigen zu bringen (sei es für Python-Ausgabe oder ausführbare Dateien, die von Python gestartet wurden), die ich vorgeschlagen habe, war es nicht erfolgreich, die Terminalausgabe zu unterdrücken. Ich habe meinen Code in eine Bearbeitung der Frage eingefügt. Hätten Sie eine Idee, wie Sie es ändern können, damit die Terminalausgabe von 'print' und' ls' unterdrückt wird? – d3pd

+0

Ah, das ist es. Es gab eine Notwendigkeit, mit stdout umzugehen. Nochmals vielen Dank für Ihre klare und detaillierte Lösung. – d3pd

2

Insbesondere für die ALSA-Fehler können Sie ALSAs snd_lib_error_set_handler-Funktion wie für Beispiel in this question beschrieben verwenden.

Für eine meiner Projekte habe ich ein Modul mute_alsa genannt, die wie folgt aussieht:

import ctypes 

ERROR_HANDLER_FUNC = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int, 
             ctypes.c_char_p, ctypes.c_int, 
             ctypes.c_char_p) 


def py_error_handler(filename, line, function, err, fmt): 
    pass 

c_error_handler = ERROR_HANDLER_FUNC(py_error_handler) 

try: 
    asound = ctypes.cdll.LoadLibrary('libasound.so.2') 
    asound.snd_lib_error_set_handler(c_error_handler) 
except OSError: 
    pass 

Und Sie es verwenden, einfach, indem sie die folgenden in Ihren Code:

import mute_alsa 

In ein allgemeinerer Sinn, können Sie verhindern, alles Drucken stderr durch einfaches Schließen der zugeordneten Dateideskriptor.Für Beispiel, wenn wir rm eine Datei versuchen, die nicht vorhanden ist, erhalten wir eine Fehler Nachricht an stderr gedruckt:

>>> import sys 
>>> import os 
>>> os.system('rm /doesnotexist') 
rm: cannot remove ‘/doesnotexist’: Is a directory 
256 

Wenn wir schließen die stderr Deskriptordatei:

>>> os.close(sys.stderr.fileno()) 

Wir keine Fehlermeldungen mehr angezeigt:

>>> os.system('rm /doesnotexist') 

Der Nachteil dieses Ansatzes ist, dass es alle Nachrichten an stderr, was bedeutet, dass Sie nicht mehr legitime Fehler sehen Nachrichten zum Schweigen bringt. Dies kann zu überraschenden Ausfallarten führen (das Programm ohne Druck Fehler verlassen!).

+0

Hey, vielen Dank für Ihre detaillierte Lösung (und für die präventive ALSA-Stummschaltung). Wenn ich 'os.close (sys.stdout.fileno())' und 'os.close (sys.stderr.fileno())' in die Funktionen enter oder init des obigen Kontextmanagers einfüge, wird dies von der Terminalausgabe nicht gemacht scheinen unterdrückt zu werden. Kannst du sehen, was ich vermisse? – d3pd

+0

Das ist großartig, danke! – Eugene

Verwandte Themen