2015-12-24 50 views
5

Kiedy uruchomić następujący kodPython podproces komunikować() daje Brak, gdy oczekuje się lista numer

from subprocess import call, check_output, Popen, PIPE 

gr = Popen(["grep", "'^>'", myfile], stdout=PIPE) 
sd = Popen(["sed", "s/.*len=//"], stdin=gr.stdout) 
gr.stdout.close() 
out = sd.communicate()[0] 
print out 

Gdzie myfile wygląda następująco:

>name len=345 
sometexthere 
>name2 len=4523 
someothertexthere 
... 
... 

uzyskać

None 

Gdy oczekiwane wyjście jest listą liczb:

345 
4523 
... 
... 

odpowiednie polecenie uruchomić w terminalu jest

grep "^>" myfile | sed "s/.*len=//" > outfile 

Dotychczas próbowałem zabawy z ucieczki i powołując się na różne sposoby, takie jak ucieczka ukośniki w SED lub dodanie dodatkowych cudzysłowów dla grep, ale możliwości kombinatoryczne są duże.

Rozważałem też tylko czytanie w pliku i pisanie odpowiedników grep i sed w Pythonie, ale plik jest bardzo duży (zawsze mogłem czytać wiersz po linii), zawsze będzie działał w systemach UNIX i ja nadal jestem ciekawy, gdzie popełniłem błędy.

Czyżby

sd.communicate()[0] 

zwraca jakiś obiekt (zamiast listy liczb całkowitych), dla których żaden nie jest typ?

wiem, że mogę chwycić wyjście z check_output w prostych przypadkach:

sam = check_output(["samn", "stats", myfile]) 

ale nie wiem jak zrobić to praca z bardziej skomplikowanych sytuacjach były rzeczy jest coraz rurami.

Jakie są produktywne podejścia do uzyskania oczekiwanych wyników z podprocesu?

Odpowiedz

4

Jak sugeruje trzeba stdout=PIPE w drugim procesie i usunąć apostrofów od "'^>'":

gr = Popen(["grep", "^>", myfile], stdout=PIPE) 
Popen(["sed", "s/.*len=//"], stdin=gr.stdout, stdout=PIPE) 
...... 

Ale można to zrobić tylko przy użyciu czystego Python i re:

import re 
r = re.compile("^\>.*len=(.*)$") 
with open("test.txt") as f: 
    for line in f: 
     m = r.search(line) 
     if m: 
      print(m.group(1)) 

Który wyświetli:

345 
4523 

Jeśli linie zaczynające się > zawsze mieć numer i numer jest zawsze na końcu po len= wtedy nie faktycznie trzeba regex albo:

with open("test.txt") as f: 
    for line in f: 
     if line.startswith(">"): 
      print(line.rsplit("len=", 1)[1]) 
+1

nie stosować 'check_output()' tutaj: może zawiesić 'proces grep' podaniu 'sed' umiera przedwcześnie (do GC zamyka rurę gr.stdout'' w rodzic). Aby uniknąć wywołania '.close()', zacznij od tyłu - zobacz [Jak korzystać z podprocesu.Popen do łączenia wielu procesów przez potoki?] (Http://stackoverflow.com/a/9164238/4279) – jfs

+0

@JF Sebastian, właśnie go usunąłem, ponieważ nie ma potrzeby wywoływania podprocesu w ogóle, także co to jest 'gc'? –

+0

Tak, tutaj nie ma potrzeby podprocesu. gc to zbieranie śmieci. – jfs

2

Musisz przekierować stdout na drugim Popen połączenia lub wyjście będzie po prostu pójść do stdout procesu nadrzędnego i communicate powróci None.

sd = Popen(["sed", "s/.*len=//"], stdin=gr.stdout, stdout=PIPE) 
4
  1. Nie umieszczać apostrofów wokół ^> w linii grep. To nie jest bash, więc wszystkie argumenty będą dosłownie przekazywane do bazowego programu.
  2. Musisz przekierować stdout sd do PIPE.
1

Padraic Cunningham odpowiedź jest dopuszczalne

Jak stosować apostrofów w linii poleceń ciąg

use shlex 

.

import shlex 
from subprocess import call, check_output, Popen, PIPE 
gr = Popen(shlex.split("grep '^>' my_file"), stdout=PIPE) 
sd = Popen(["sed", "s/.*len=//"], stdin=gr.stdout,stdout=PIPE) 
gr.stdout.close() 
out = sd.communicate()[0] 
print out