2009-11-16 17 views
49

He escrito una utilidad de línea de comandos que usa getopt para analizar argumentos dados en la línea de comando. También me gustaría tener un nombre de archivo sea un argumento opcional, como lo es en otras utilidades como grep, corte, etc. Por lo tanto, me gustaría tener el siguiente usoLeer desde archivo, o STDIN

tool -d character -f integer [filename] 

¿Cómo puedo aplicar la siguiente ?

  • si se da un nombre de archivo, lea del archivo.
  • si no se proporciona un nombre de archivo, lea de STDIN.
+2

vea también http://unix.stackexchange.com/questions/47098/how-do-i-make-python-programs-behave-like-proper-unix-tools/47543#47543 – magnetar

Respuesta

47

En los términos más simples:

import sys 
# parse command line 
if file_name_given: 
    inf = open(file_name_given) 
else: 
    inf = sys.stdin 

En este punto se usaría inf a leer el archivo. Dependiendo de si se dio un nombre de archivo, esto se leería desde el archivo dado o desde stdin.

Cuando tenga que cerrar el archivo, se puede hacer esto:

if inf is not sys.stdin: 
    inf.close() 

Sin embargo, en la mayoría de los casos quedará inofensivo para cerrar sys.stdin si está terminado con él.

+0

¿Raw_input() y input() leerán inf? – thefourtheye

+0

@thefourtheye: Sí, ambas funciones se leerán desde un archivo o desde 'sys.stdin'. –

+2

Encontré otra manera de resolver este problema, publiqué sobre esto aquí http://dfourtheye.blogspot.in/2013/05/python-equivalent-of-cs-freopen.html y agregué una respuesta a esta pregunta también. – thefourtheye

61

El módulo fileinput puede hacer lo que quiera - suponiendo que los argumentos que no sean opciones están en args a continuación:

import fileinput 
for line in fileinput.input(args): 
    print line 

Si args es vacía, entonces fileinput.input() leerá de la entrada estándar; de lo contrario, se lee de cada archivo uno por uno, de manera similar al while(<>) de Perl.

+0

Esto fue tan bueno de una respuesta, pero no es tan generalizable. Recordaré usar la entrada de archivo la próxima vez, si corresponde. –

+0

Funciona sin 'args' también. – Gabriel

+0

Correcto, pero si usa 'getargs' (como OP), probablemente solo quiera pasar los argumentos restantes en lugar de' sys.argv [1:] '(que es el predeterminado). – SimonJ

0

Algo así como:

if input_from_file: 
    f = open(file_name, "rt") 
else: 
    f = sys.stdin 
inL = f.readline() 
while inL: 
    print inL.rstrip() 
    inL = f.readline() 
8

para hacer uso de la declaración del pitón with, se puede utilizar el siguiente código:

import sys 
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f: 
    # read data using f 
    # ...... 
+0

Su solución cerrará 'sys.stdin', por lo que las llamadas a la función' input' después de 'con la instrucción' levantará 'ValueError'. –

5

Yo prefiero usar "-" como un indicador de que usted debe leer de stdin, es más explícito:

import sys 
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f: 
    pass # do something here 
+2

Su solución cerrará 'sys.stdin', por lo que las llamadas a la función' input' después de 'con la instrucción' levantará 'ValueError'. –

+1

@TimofeyBondarev Eso puede ser cierto ... pero lo más frecuente es que la entrada solo se use una vez en un script. Esta es una construcción útil. – javadba

11

Me gusta el lenguaje general de usar un contexto de gestión er, pero la solución (demasiado) trivial termina cerrando sys.stdin cuando está fuera de la declaración with, que quiero evitar.

préstamos de this answer, aquí es una solución:

import sys 
import contextlib 

@contextlib.contextmanager 
def _smart_open(filename, mode='Ur'): 
    if filename == '-': 
     if mode is None or mode == '' or 'r' in mode: 
      fh = sys.stdin 
     else: 
      fh = sys.stdout 
    else: 
     fh = open(filename, mode) 
    try: 
     yield fh 
    finally: 
     if filename is not '-': 
      fh.close() 

if __name__ == '__main__': 
    args = sys.argv[1:] 
    if args == []: 
     args = ['-'] 
    for filearg in args: 
     with _smart_open(filearg) as handle: 
      do_stuff(handle) 

supongo que se podría lograr something similar with os.dup() pero el código cociné hasta hacer eso resultó ser más complejo y más mágico, mientras que el anterior es un tanto torpe pero muy directo.

+0

¡Muchas gracias! Esto es exactamente lo que estaba buscando. Solución muy clara y directa. – edisonex

Cuestiones relacionadas