2009-04-22 14 views
7

Questo codice genera "AttributeError: oggetto 'Popen' non ha alcun attributo 'Fileno'" quando eseguito con Python 2.5.1Python sottoprocesso "oggetto non ha attributo 'Fileno'" errore

Codice:

def get_blame(filename): 
    proc = [] 
    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
    proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 
    proc.append(Popen(['tr', r"'\040'", r"';'"], stdin=proc[-1]), stdout=PIPE) 
    proc.append(Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=proc[-1]), stdout=PIPE) 
    return proc[-1].stdout.read() 

Stack:

function walk_folder in blame.py at line 55 
print_file(os.path.join(os.getcwd(), filename), path) 

function print_file in blame.py at line 34 
users = get_blame(filename) 

function get_blame in blame.py at line 20 
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

function __init__ in subprocess.py at line 533 
(p2cread, p2cwrite, 

function _get_handles in subprocess.py at line 830 
p2cread = stdin.fileno() 

Questo codice dovrebbe funzionare il pitone documenti descrivono this usage.

+4

La chiusura di questo come "troppo localizzata" è ridicolo come * I * trovato utile, anni dopo. Questa è la natura dei problemi di programmazione: sono sempre problemi di nicchia applicabili a una base di utenti ristretta. Ma SONO utili ... SO mi rende triste in questi giorni, ed è per questo che non lo uso più spesso. – Dan

risposta

10

Tre cose

In primo luogo, le tue() sono errate.

In secondo luogo, il risultato di subprocess.Popen() è un oggetto del processo, non un file.

proc = [] 
proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

Il valore di proc[-1] non è il file, è il processo che contiene il file.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE)) 

terzo luogo, non fare tutto questo tr e cut spazzatura nel guscio, alcune cose potrebbero essere più lento. Scrivi l'elaborazione tr e cut in Python: è più veloce e più semplice.

+0

Era l'albero per l'umorismo o intendevi "tre"? –

+0

Grazie per il commento. È sistemato. –

+0

Lo script sarà usato solo su linux. È più uno strumento singolo che altro. Usare gli strumenti di shell era più facile che cercare di capire l'equivalente Python. Basta abbandonare un comando di shell funzionante è più facile del debugging regex. – epochwolf

-1

sembra un errore di sintassi. tranne prima appendere il resto sono errati (parentesi graffe).

+0

In realtà non è così. – epochwolf

+0

non è cosa? ogni singola risposta includeva il fatto che hai un problema con le tue parentesi! si tratta di un problema di sintassi, ovviamente in aggiunta ai piping. – SilentGhost

+0

È un problema di sintassi ma non un errore di sintassi abbastanza strano. Il codice è stato incollato direttamente dallo script che avevo. L'errore era l'errore che è stato generato. Un errore di sintassi avrebbe ucciso lo script nel mezzo della definizione della funzione. La funzione viene effettivamente chiamata. Penso che sia perché list.append() accetta più argomenti. – epochwolf

1

si desidera che lo stdout del processo, in modo da sostituire il tuo stdin=proc[-1] con stdin=proc[-1].stdout

Inoltre, è necessario spostare il parentesi, dovrebbe venire dopo l'argomento stdout.

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1]), stdout=PIPE) 

dovrebbe essere:

proc.append(Popen(['tr', '-s', r"'\040'"], stdin=proc[-1].stdout, stdout=PIPE)) 

risolvere il tuo altri append chiamate nello stesso modo.

3

Ci sono alcune cose strane nella sceneggiatura,

  • Perché sei la memorizzazione di ogni processo in un elenco? Non sarebbe molto più leggibile usare semplicemente le variabili? Eliminando tutti i .append()s rivela un errore di sintassi, più volte avete superato stdout = tubo al append argomenti, invece di Popen:

    proc.append(Popen(...), stdout=PIPE) 
    

    Quindi, un diritto-riscrittura (ancora con errori citerò in un secondo) sarebbe diventato ..

    def get_blame(filename): 
        blame = Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE) 
        tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame, stdout=PIPE) 
        tr2 = Popen(['tr', r"'\040'", r"';'"], stdin=tr1), stdout=PIPE) 
        cut = Popen(['cut', r"-d", r"\;", '-f', '3'], stdin=tr2, stdout=PIPE) 
        return cut.stdout.read() 
    
  • Su ogni successivo comando, avete superato l'oggetto popen, non che elabora stdout. Dalla sezione "Replacing shell pipeline" dei documenti di processo parziali, si fa ..

    p1 = Popen(["dmesg"], stdout=PIPE) 
    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) 
    

    ..mentre stavi facendo l'equivalente di stdin=p1.

    Il tr1 = (nel codice riscritto sopra) linea diventerebbe ..

    tr1 = Popen(['tr', '-s', r"'\040'"], stdin=blame.stdout, stdout=PIPE) 
    
  • Non è necessario per sfuggire comandi/argomenti con sottoprocesso, come sottoprocesso non viene eseguito il comando a qualsiasi shell (a meno che non si specifica shell=True). Vedere la sezione Security dei documenti di sottoprocesso.

    Invece di ..

    proc.append(Popen(['svn', 'blame', shellquote(filename)], stdout=PIPE)) 
    

    ..si può fare tranquillamente ..

    Popen(['svn', 'blame', filename], stdout=PIPE) 
    
  • Come suggerito S. Lott, non utilizzare il sottoprocesso per fare di testo manipolazioni più facile fatto in Python (i comandi tr/cut). Per esempio, tr/cut ecc non sono estremamente portabili (versioni diverse hanno argomenti diversi), anche loro sono piuttosto difficili da leggere (non ho idea di cosa stiano facendo il tr e il cut)

    Se dovessi riscrivere il comando, io probabilmente fare qualcosa di simile ..

    def get_blame(filename): 
        blame = Popen(['svn', 'blame', filename], stdout=PIPE) 
        output = blame.communicate()[0] # preferred to blame.stdout.read() 
        # process commands output: 
        ret = [] 
        for line in output.split("\n"): 
         split_line = line.strip().split(" ") 
         if len(split_line) > 2: 
          rev = split_line[0] 
          author = split_line[1] 
          line = " ".join(split_line[2:]) 
    
          ret.append({'rev':rev, 'author':author, 'line':line}) 
    
        return ret 
    
-2

come S. Lott ha detto, elaborando il testo in Python è meglio.

Ma se si desidera utilizzare le utilità cmdline, si può conservare leggibile utilizzando shell=True:

cmdline = r"svn blame %s | tr -s '\040' | tr '\040' ';' | cut -d \; -f 3" % shellquote(filename) 
return Popen(cmdline, shell=True, stdout=PIPE).communicate()[0] 
+1

C'è un enorme rischio per la sicurezza nell'uso di shell = True, oltre a una reale possibilità che il processo possa rimanere sospeso tra i dati di connessione da un comando a quello successivo senza fornire alcuna reale gestione degli errori. – SummerEla

+0

@SummerEla abbastanza giusto, anche se è un compromesso - le persone lo fanno in script di shell per tutto il tempo e lo accetto lì. E 'possibile fare lo stesso compromesso quando scripting in Python pure. La leggibilità e la semplicità hanno il loro valore troppo – orip

Problemi correlati