Question

Il est question de suivi de .


Là où je veux aller do

Je voudrais être en mesure de rediriger temporairement le stdout dans un fichier temporaire, alors que python est encore capable d'imprimer à stdout. Cela implique les étapes suivantes:

  1. Créer une copie de stdout (new)
  2. Créer un fichier temporaire (de tmp)
  3. stdout dans tmp Redirect
  4. python suggèrent d'utiliser new comme stdout
  5. Redirect tmp dans le "réel" stdout
  6. python suggèrent d'utiliser le stdout "vrai" nouveau
  7. Lire et à proximité tmp

Mise en œuvre

J'ai essayé de mettre en œuvre ce qui précède la manière suivante:

import os
import subprocess
import sys

#A function that calls an external process to print to stdout as well as
#a python print to pythons stdout.
def Func(s, p = False):
    subprocess.call('echo "{0}"'.format(s), shell = True)
    if p:
        print "print"

sil = list() # <-- Some list to store the content of the temp files

print "0.1" # Some testing of the
Func("0.2") # functionality

new = os.dup(1)    # Create a copy of stdout (new)
tmp = os.tmpfile() # Create a temp file (tmp)

os.dup2(tmp.fileno(), 1)            # Redirect stdout into tmp
sys.stdout = os.fdopen(new, 'w', 0) # Tell python to use new as stdout

Func("0.3", True) # <--- This should print "0.3" to the temp file and "print" to stdout

os.dup2(new, 1)                   # Redirect tmp into "real" stdout
sys.stdout = os.fdopen(1, 'w', 0) # Tell python to use "real" stdout again

# Read and close tmp
tmp.flush()
tmp.seek(0, os.SEEK_SET)
sil.append(tmp.read())
tmp.close()

Je voudrais faire une petite pause ici pour résumer.
La sortie à la console jusqu'à ici doit se lire:

0.1
0.2
print

alors sil devrait ressembler à ceci: ['0.3\n']. Donc, tout fonctionne comme un haut charme jusqu'à ici. Cependant, si je refais le script ci-dessus à nouveau comme ceci:

print "1.1" # Some testing of the
Func("1.2") # functionality

new = os.dup(1)    # Create a copy of stdout (new)
tmp = os.tmpfile() # Create a temp file (tmp)

os.dup2(tmp.fileno(), 1)            # Redirect stdout into tmp
sys.stdout = os.fdopen(new, 'w', 0) # Tell python to use new as stdout

# This should print "0.3" to the temp file and "print" to stdout and is the crucial point!
Func("1.3", True) 

os.dup2(new, 1)                   # Redirect tmp into "real" stdout
sys.stdout = os.fdopen(1, 'w', 0) # Tell python to use "real" stdout again

# Read and close tmp
tmp.flush()
tmp.seek(0, os.SEEK_SET)
sil.append(tmp.read())

une erreur se produit et les regards de sortie comme ceci:

1.1
1.2
/bin/sh: line 0: echo: write error: Bad file descriptor
print

en sil lit comme suit:. ['0.3\n', '']

En d'autres termes:. Le second Func("1.3", True) ne peut pas écrire dans le fichier temp

Questions

  1. Tout d'abord, je voudrais savoir pourquoi ne fonctionne pas mon script comme je veux travailler. Ce qui signifie, pourquoi est-il possible que dans la première moitié du script pour écrire dans le fichier temporaire?
  2. Je suis encore un peu perplexe par l'utilisation de dup et dup2. Bien que je pense que je comprends comment la redirection de stdout dans un fichier temporaire fonctionne totalement, je ne sais maintenant pourquoi os.dup2(new, 1) fait ce qu'il fait. Peut-être que la réponse pourrait élaborer sur ce que tous les dup et dup2s dans mon script font ^^
Était-ce utile?

La solution

La raison pour laquelle vous obtenez un « mauvais descripteur de fichier » est que le garbage collector ferme la FD stdout pour vous. Tenez compte de ces deux lignes:

sys.stdout = os.fdopen(1, 'w', 0)    # from first part of your script
...
sys.stdout = os.fdopen(new, 'w', 0)  # from second part of your script

Maintenant, quand la seconde de ces deux sont exécutés le compte de référence du premier objet de fichier tombe à zéro et le garbage collector détruit. objets de fichiers fermer leur fd associés lorsque destructed, et que fd se trouve être 1 = stdout. Donc, vous devez être très prudent avec la façon dont vous détruisez les objets créés avec os.fdopen.

Voici un petit exemple pour montrer le problème. os.fstat est simplement utilisé comme un exemple de fonction qui déclenche l'erreur « descripteur fichier incorrect » lorsque vous passez un fd fermé.

import os
whatever = os.fdopen(1, 'w', 0)
os.fstat(1)
del whatever
os.fstat(1)

Je se fait d'avoir un gestionnaire de contexte que je pense que fait exactement (ou presque atleast, dans mon cas, je Happen besoin d'un nom tempfile) ce que vous recherchez. Vous pouvez voir qu'il réutilise l'objet sys.stdout d'origine pour éviter la fermeture problématique.

import sys
import tempfile
import os

class captured_stdout:
    def __init__(self):
        self.prevfd = None
        self.prev = None

    def __enter__(self):
        F = tempfile.NamedTemporaryFile()
        self.prevfd = os.dup(sys.stdout.fileno())
        os.dup2(F.fileno(), sys.stdout.fileno())
        self.prev = sys.stdout
        sys.stdout = os.fdopen(self.prevfd, "w")
        return F

    def __exit__(self, exc_type, exc_value, traceback):
        os.dup2(self.prevfd, self.prev.fileno())
        sys.stdout = self.prev

## 
## Example usage
##

## here is a hack to print directly to stdout
import ctypes
libc=ctypes.LibraryLoader(ctypes.CDLL).LoadLibrary("libc.so.6")
def directfdprint(s):
    libc.write(1, s, len(s))


print("I'm printing from python before capture")
directfdprint("I'm printing from libc before captrue\n")

with captured_stdout() as E:
    print("I'm printing from python in capture")
    directfdprint("I'm printing from libc in capture\n")

print("I'm printing from python after capture")
directfdprint("I'm printing from libc after captrue\n")

print("Capture contains: " + repr(file(E.name).read()))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top