2011-07-18 16 views
16

Estoy tratando de implementar algo similar a git log, que solo mostrará la salida si el registro tiene una cierta longitud. Si usted no está familiarizado con git, estoy esencialmente tratando de lograr esto:Paging output from python

python some_script.py | less 

Con la ayuda de la aplicación de paginación en python2.6/pydoc.py, yo era capaz de llegar con esto:

import os 
text = '...some text...' 
pipe = os.popen('less', 'w') 
pipe.write(text) 
pipe.close() 

que funciona genial, pero os.popen() está en desuso. He considerado escribir en un archivo temporal y llamar menos con su ruta, pero eso no parece ideal. ¿Es esto posible con un subproceso? ¿Alguna otra idea?

EDIT:

Así que he recibido de trabajo subproceso. Yo era capaz de darle la variable de texto con Popen.communicate(text), pero ya que lo que realmente quiero redireccionar declaraciones de impresión, me he decidido por este:

import os, sys, subprocess, tempfile 

    page = True 
    if page: 
     path = tempfile.mkstemp()[1] 
     tmp_file = open(path, 'a') 
     sys.stdout = tmp_file 
    print '...some text...' 
    if page: 
     tmp_file.flush() 
     tmp_file.close() 
     p = subprocess.Popen(['less', path], stdin=subprocess.PIPE) 
     p.communicate() 
     sys.stdout = sys.__stdout__  

Por supuesto, que terminaría siendo envolviéndolo en funciones. ¿Alguien ve un problema con eso?

+0

Algunas observaciones: (1) el nombre del archivo temporal es único: el modo de apertura debe ser 'w', no 'a' (imposible de agregar al archivo). (2) No es necesario 'cerrar()' el archivo antes de leerlo. (3) No hay necesidad de comunicarse con el proceso de buscapersonas (basta con un simple 'subprocess.call()'). (4) Es más explícito no alterar un global como 'sys.stdout'; a menos que realmente necesite hacer esto (como si desea redirigir la salida de todos los submódulos que utiliza), es una buena idea llamar explícitamente una función de impresión especial. – EOL

+0

Este es un tema similar con una respuesta agradable y concisa: http://stackoverflow.com/questions/37584717/how-to-write-python-script-with-man-page-like-out-put/ – user3019074

Respuesta

3
+0

Sí, eso fue lo primero que intenté Al principio no funcionó, pero es ahora. Gracias. – nren

+0

Claro. Te lo dí a ti. Esperaba algún consejo más general de implementación si tienes alguno - ver mi edición. – nren

+0

Desearía poder ayudarlo más, pero ahora mismo estoy en Windows y ninguno de estos me funciona, incluso cuando se proporciona la ruta completa de mi MSYS suministrado menos. – agf

4

Es una buena idea para ser explícita en el código, por lo que muestra que se utiliza una función de impresión especiales printc() en lugar de la estándar. El uso de subprocess.call() también es suficiente (no necesita la maquinaria de tubería). Además, puede guardar una variable no se puede almacenar el nombre del archivo temporal:

from __future__ import print_function 

import subprocess, tempfile 

page = True # For tests 

# Definition of a printc() function that prints to the correct output 
if page: 
    tmp_file = open(tempfile.mkstemp()[1], 'w') # No need to store the name in a specific variable 
    def printc(*largs, **kwargs): 
     if 'file' not in kwargs: # The code can still use the usual file argument of print() 
      kwargs['file'] = tmp_file # Forces the output to go to the temp file 
     print(*largs, **kwargs) 
else: 
    printc = print # Regular print 

# Main program: 

printc('...some text...', 'some more text', sep='/') # Python3 syntax 

# Paging of the current contents of the temp file: 
if page: 
    tmp_file.flush() # No need to close the file: you can keep printing to it 
    subprocess.call(['less', tmp_file.name]) # Simpler than a full Popen() 

De esta manera, se obtiene la flexibilidad de la función de Python 3 print, con un código que muestra explícitamente que usted está haciendo un poco de fantasía impresión de cosas. Esto se escala mejor con programas más grandes que modificando la variable "global" sys.stdout en algunas ubicaciones de su código.

+1

¡Gracias por el consejo! He agregado tus optimizaciones. Necesito redirigir el resultado del submódulo, como mencionaste en el comentario, por lo que parece que estoy atascado con el enfoque sys.stdout. He puesto todo esto en una clase, por lo que se puede llamar con 'pager = Pager()' y 'pager.begin()'. – nren

28

¿Qué tal esto:

import pydoc 
text = '... some text ... ' 
pydoc.pager(text) 

Este (en mi máquina Linux openSUSE) envía el texto a un buscapersonas ('menos' en mi caso), y trabaja lo mismo que llamar "ayuda (... comando python ...) "dentro del intérprete de Python.

+3

Aunque dudo que esta función no esté disponible en el futuro, [no está documentada] (https://docs.python.org/3.5/library/pydoc.html), desafortunadamente, por lo que en principio no está completamente garantizado que seguirá estando disponible. Sin embargo, dado lo conveniente que es, supongo que este es un riesgo que a menudo valdría la pena. Además, el [código fuente] (https://hg.python.org/cpython/file/3.5/Lib/pydoc.py#l1406) parece que se puede copiar fácilmente de todos modos. – EOL

+0

Rompe mi salida cuando intento imprimir un formato de datos json. –

2

No me gustó ejecutar comandos externos, así que escribí pager en Python puro. Todavía tiene un problema: la entrada por canal funciona solo para Windows.

+0

El módulo de buscapersonas no funciona con unicode :( – MichaelR

+0

@MichaelR, abra un informe de errores aquí - https://bitbucket.org/techtonik/python-pager/issues para que no se pierda. –