Quiero contar las apariciones de alrededor de 500 patrones en un archivo .fastq grande (59 millones de líneas). Los patrones son todos exactamente 20 caracteres de longitud.
En Unix esto sería un simple:
grep -F -o -f patterns.txt big_file.fastq | sort | uniq -c
Sin embargo, me gustaría evitar escribir un archivo de patrones temporales así que creé una tubería usando la biblioteca de subprocesos de Python:
from subprocess import Popen, PIPE, STDOUT
p1 = Popen(["grep", "-F", "-o", "-f", "-", "big_file.fastq"], shell = False, stdin = PIPE, stdout = PIPE, stderr= STDOUT)
p2 = Popen(["sort"], shell = False, stdin = p1.stdout, stdout = PIPE, stderr = STDOUT)
p3 = Popen(["uniq", "-c"], shell = False, stdin = p2.stdout, stdout = PIPE, stderr = STDOUT)
entonces me comunico llamada () en este, proporcionando un tipo fichero codificado io.StringIO objeto como entrada (que se pasa al comando grep usando el '-'):
import io
patterns_file = io.StringIO("\n".join(patterns_list))
p3.communicate(input = patterns_file.read().encode('utf-8'))[0]
Cuando llamo a comunicar () en uniq así, esto funciona bien.
Sin embargo, mientras que las pruebas que erróneamente llamado en la primera parte de la tubería:
p1.communicate(input = patterns_file.read().encode('utf-8'))[0]
Esto me dio salidas completamente equivocadas, incluyendo partidos que eran más corto o más largo que el espera que 20 caracteres.
No entiendo por qué es. no llamar comunicarse () p1 involucra solamente la parte de la tubería e ignorar el resto? Extracción P2 y P3 P1 causado a grep correctamente. Siento que estoy perdiendo algo acerca de cómo funciona Popen.
Cualquier ayuda es apreciada.
Al crear una instancia Popen
objetos, los subprocesos que se refieren se inician inmediatamente. Por lo tanto, incluso si sólo se llama communicate()
en p1
, p2
y p3
también se está ejecutando.
¿Por qué esto es importante? Debido a que p2
todavía tiene su entrada estándar adjunto a la FIFO a la que p1
está escribiendo su salida!
Si la sort
operación en p2
todavía está leyendo el contenido al mismo tiempo que le pida a su programa de Python para leer el mismo contenido directamente, se termina con p1
la salida 's repartirse entre ellos. Hilaridad se puede esperar que ensue; sólo si tanto p2
y communicate()
estaban leyendo bloques que eran múltiplos de 20 bytes serían las divisiones estar sucediendo de forma fiable en los límites de registro, y la porción típica tamaños utilizados para lee sin búfer son múltiplos de 4096.
Por cierto - para muchos programas que esto no tendría un efecto tan grave, porque FIFO tienen una proporción relativamente pequeña de amortiguación; un programa que escribe una línea de salida para cada línea de entrada se lee terminaría bloqueado en la salida muy rápidamente, y por lo tanto sería dejar de leer más de entrada hasta su salida es al menos parcialmente enrojecida. sort
es una excepción, ya que tiene que leer todo su entrada antes de que se sabe cuál será su primera línea de salida!