Leer desde Archivo, o STDIN


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

tool -d character -f integer [filename]

¿Cómo puedo implementar lo siguiente?

  • si se da un nombre de archivo, lea desde el archivo.
  • si no se da un nombre de archivo, lea desde STDIN.
Author: Ryan R. Rosario, 2009-11-17

7 answers

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 usarías inf para leer desde el archivo. Dependiendo de si se le dio un nombre de archivo, esto se leería desde el archivo dado o desde stdin.

Cuando necesite cerrar el archivo, puede hacer esto:

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

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

 50
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-16 21:40:54

El módulo fileinput puede hacer lo que quiera, asumiendo que los argumentos que no son opciones están en args entonces:

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

Si args está vacío, entonces fileinput.input() leerá desde stdin; de lo contrario, lee de cada archivo a su vez, de manera similar a while(<>) de Perl.

 69
Author: SimonJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-16 21:44:19

Me gusta el modismo general de usar un administrador de contexto, pero la solución (demasiado) trivial termina cerrándose sys.stdin cuando estás fuera de la instrucción with, lo que quiero evitar.

Tomando prestado de esta respuesta , aquí hay 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 podrías lograr algo similar con os.dup() pero el código que inventé para hacer eso resultó ser más complejo y más mágico, mientras que el anterior es algo torpe pero muy sencillo.

 13
Author: tripleee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:12

Para hacer uso de la instrucción with de python, se puede usar 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
    # ......
 8
Author: Roun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-06 01:49:06

Prefiero usar " - " como un indicador que 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
 7
Author: Phil L.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-25 21:00:51

No respuesta directa sino relacionada.

Normalmente cuando escribes un script en python puedes usar el paquete argparse. SI este es el caso se puede utilizar:

parser = argparse.ArgumentParser()
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)

'?'. Si es posible, se consumirá un argumento desde la línea de comandos, y producido como un solo artículo. Si no hay ningún argumento de línea de comandos presente, se producirá el valor predeterminado.

Y aquí establecemos por defecto sys.stdin;

Así que si hay un archivo lo leerá , y si no tomará el input from stdin "Nota: que estamos usando el argumento posicional en el ejemplo anterior"

Para más visita: https://docs.python.org/2/library/argparse.html#nargs

 1
Author: Medhat Helmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-24 09:29:54

Algo 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()
 0
Author: GreenMatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-11-16 21:54:11