我编写了一个命令行实用程序,它使用 getopt 来解析命令行上给出的参数。我还想让文件名成为可选参数,例如在 grep、cut 等其他实用程序中。所以,我希望它具有以下用法
tool -d character -f integer [filename]
我该如何实施以下内容?
- 如果给出了文件名,则从文件中读取。
- 如果没有给出文件名,则从 STDIN 读取。
我编写了一个命令行实用程序,它使用 getopt 来解析命令行上给出的参数。我还想让文件名成为可选参数,例如在 grep、cut 等其他实用程序中。所以,我希望它具有以下用法
tool -d character -f integer [filename]
我该如何实施以下内容?
fileinput模块可以做你想做的事——假设非选项参数在里面args
:
import fileinput
for line in fileinput.input(args):
print line
如果args
为空fileinput.input()
,则从标准输入读取;否则它会依次从每个文件中读取,方式类似于 Perl 的while(<>)
.
用最简单的话来说:
import sys
# parse command line
if file_name_given:
inf = open(file_name_given)
else:
inf = sys.stdin
此时您将使用inf
从文件中读取。根据是否给出了文件名,这将从给定文件或标准输入中读取。
当你需要关闭文件时,你可以这样做:
if inf is not sys.stdin:
inf.close()
但是,在大多数情况下,sys.stdin
如果你完成了它,关闭它是无害的。
我喜欢使用上下文管理器的一般习惯用法,但是(太)微不足道的解决方案最终会sys.stdin
在您退出with
语句时关闭,我想避免这种情况。
借用这个答案,这是一个解决方法:
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 != '-':
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)
我想你可以实现类似的东西,os.dup()
但是我编写的代码变得更复杂、更神奇,而上面的代码有点笨拙但非常简单。
我更喜欢使用“-”作为您应该从标准输入读取的指示符,它更明确:
import sys
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f:
pass # do something here
要使用 python 的with
语句,可以使用以下代码:
import sys
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f:
# read data using f
# ......
不是直接的答案,而是相关的。
通常,当您编写 python 脚本时,您可以使用该argparse
包。如果是这种情况,您可以使用:
parser = argparse.ArgumentParser()
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
'?'。如果可能,将从命令行使用一个参数,并将其作为单个项目生成。如果不存在命令行参数,则将生成默认值。
在这里我们将默认设置为sys.stdin
;
所以如果有一个文件,它将读取它,如果没有,它将从标准输入获取输入“注意:我们在上面的示例中使用了位置参数”
切换到argparse
(它也是标准库的一部分)并使用
argparse.FileType
默认值 stdin:
import argparse, sys
p = argparse.ArgumentParser()
p.add_argument('input', nargs='?',
type=argparse.FileType(), default=sys.stdin)
args = p.parse_args()
print(args.input.readlines())
但是,这不会让您为标准输入指定编码和其他参数;如果你想这样做,你需要使参数成为非可选参数,并在作为参数给出FileType
时使用 stdin 做它的事情:-
p.add_argument('input', type=FileType(encoding='UTF-8'))
注意后一种情况将不支持二进制模式 ( 'b'
) I/O。如果您只需要它,您可以使用上面的默认参数技术,但提取二进制 I/O 对象,例如default=sys.stdout.buffer
用于 stdout。但是,如果用户指定,这仍然会中断-
。(使用-
stdin/stdout 总是包裹在TextIOWrapper
.)
如果您希望它与 . 一起使用-
,或者在打开文件时需要提供任何其他参数,如果包装错误,您可以修复该参数:
p.add_argument('output', type=argparse.FileType('wb'))
args = p.parse_args()
if hasattr(args.output, 'buffer'):
# If the argument was '-', FileType('wb') ignores the 'b' when
# wrapping stdout. Fix that by grabbing the underlying binary writer.
args.output = args.output.buffer
(帽子提示medhat提到add_argument()
的type
参数。)
就像是:
if input_from_file:
f = open(file_name, "rt")
else:
f = sys.stdin
inL = f.readline()
while inL:
print inL.rstrip()
inL = f.readline()