2009-11-16 17 views
49

Ich habe ein Befehlszeilenprogramm geschrieben, das getopt zum Parsen von Argumenten verwendet, die in der Befehlszeile angegeben werden. Ich möchte auch einen Dateinamen haben ein optionales Argument sein, wie es in anderen Programmen wie grep, schneiden usw. Also, ich würde es wie die folgende NutzungsAus Datei lesen, oder STDIN

tool -d character -f integer [filename] 

haben Wie kann ich implementieren die folgenden ?

  • Wenn ein Dateiname angegeben wird, lesen Sie aus der Datei.
  • Wenn ein Dateiname nicht angegeben ist, lesen Sie von STDIN.
+2

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

Antwort

47

In einfachen Worten ausgedrückt:

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

An dieser Stelle würden Sie inf verwenden aus der Datei zu lesen. Je nachdem, ob ein Dateiname angegeben wurde, würde dies von der angegebenen Datei oder von stdin gelesen werden.

Wenn Sie die Datei schließen, dann können Sie dies tun:

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

Doch in den meisten Fällen wird es harmlos sein sys.stdin zu schließen, wenn Sie mit ihm fertig sind.

+0

Werden raw_input() und input() aus inf gelesen? – thefourtheye

+0

@thefourtheye: Ja, beide Funktionen lesen entweder aus einer Datei oder aus 'sys.stdin'. –

+2

Ich fand einen anderen Weg, um dieses Problem zu lösen, ich habe hier darüber http://dfourtheye.blogspot.in/2013/05/python-equivalent-of-cs-freopen.html gebloggt und eine Antwort auf diese Frage hinzugefügt. – thefourtheye

61

Das fileinput Modul kann das tun, was Sie wollen - in args die Nicht-Option Argumente unter der Annahme, sind dann:

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

Wenn args leer ist, dann wird fileinput.input() von stdin lesen; Ansonsten liest es aus jeder Datei der Reihe nach ähnlich wie Perls while(<>).

+0

Das war genauso gut einer Antwort, aber ist nicht ganz so verallgemeinerbar. Ich werde daran denken, fileinput das nächste Mal zu verwenden, wenn es angemessen ist. –

+0

Es funktioniert auch ohne 'Args'. – Gabriel

+0

Richtig, aber wenn Sie 'getargs' verwenden (wie das OP ist), dann wollen Sie wahrscheinlich nur die übrig gebliebenen Argumente übergeben und nicht' sys.argv [1:] '(was der Standard ist). – SimonJ

0

Etwas wie:

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

Um die Verwendung der Python with Aussage zu machen, kann man den folgenden Code verwenden:

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

Ihre Lösung wird 'sys.stdin' schließen, so dass' input' Funktionsaufrufe nach 'with' Anweisung' ValueError' auslösen. –

5

ich lieber verwenden "-" als Indikator, die Sie lesen sollten von stdin, es ist expliziter:

+2

Ihre Lösung wird 'sys.stdin' schließen, so dass' input' Funktionsaufrufe nach 'with' Anweisung' ValueError' auslösen. –

+1

@TimofeyBondarev Das mag wahr sein, aber am häufigsten wird die Eingabe nur einmal in einem Skript verwendet. Dies ist ein nützliches Konstrukt. – javadba

11

Ich mag das allgemeine Idiom der Verwendung eines Kontextmanag ähm, aber die (zu) triviale Lösung endet mit dem Schließen sys.stdin, wenn Sie aus der with Anweisung herauskommen, die ich vermeiden möchte.

von this answer Leihen, hier eine Abhilfe:

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) 

Ich nehme an, Sie something similar with os.dup() erreichen könnte, aber der Code ich gekocht bis zu, dass komplexere entpuppte und magischer, während die oben etwas ist klobig, aber sehr geradlinig.

+0

Vielen Dank! Genau danach habe ich gesucht. Sehr klare und unkomplizierte Lösung. – edisonex