如何使python程序的行为像适当的unix工具?


24

我周围有一些Python脚本,我正在重写它们。我对他们所有人都有同样的问题。

对于我来说,如何编写程序以使其像适当的unix工具一样运作尚不明显。

因为这

$ cat characters | progname

和这个

$ progname characters

应该产生相同的输出。

我在Python中找到的最接近的东西是fileinput库。不幸的是,我并没有真正看到如何重写我的Python脚本,所有这些看起来像这样:

#!/usr/bin/env python 
# coding=UTF-8

import sys, re

for file in sys.argv[1:]:
    f = open(file)
    fs = f.read()
    regexnl = re.compile('[^\s\w.,?!:;-]')
    rstuff = regexnl.sub('', fs)
    f.close()
    print rstuff

如果有标准输入,fileinput库处理标准输入,如果有文件,则处理文件。但是它遍历单行。

import fileinput
for line in fileinput.input():
    process(line)

我真的不明白。我想如果您要处理的是小文件,或者您对文件的处理不多,这似乎很明显。但是,就我的目的而言,这比仅打开整个文件并将其读取为字符串要慢得多,如上所述。

目前我像上面那样运行脚本

$ pythonscript textfilename1 > textfilename2

但我希望能够在管道中运行它(及其弟兄),例如

$ grep pattern textfile1 | pythonscript | pythonscript | pythonscript > textfile2

Answers:


9

为什么不只是

files = sys.argv[1:]
if not files:
    files = ["/dev/stdin"]

for file in files:
    f = open(file)
    ...

12
sys.stdin应该改用它,因为它比硬编码的文件路径更可移植。
Piotr Dobrogost,2015年

sys.stdin如Piotr所说,应该改为使用
smci 2015年

但是sys.stdin是一个文件,并且它已经打开,并且不能关闭。不可能处理文件参数一样处理而不会跳过箍。
Alexis

@alexis当然,如果您想关闭f或想要使用上下文管理器,则需要更复杂的东西。请参阅我的新答案。
Mikel

12

检查文件名是否作为参数给出,或者从中读取sys.stdin

像这样:

if sys.argv[1]:
   f = open(sys.argv[1])
else:
   f = sys.stdin 

除了使用sys模块外,它与Mikel的答案类似。我想如果他们在那里有它一定是有原因的...


如果在命令行上指定了两个文件名怎么办?
Mikel 2012年

3
哦,绝对!我没有显示它,因为它已经显示在您的答案中。在某些时候,您必须信任用户来决定她的需求。但是,如果您认为这是最好的,请随时进行编辑。我的观点只是替换"open(/dev/stdin")sys.stdin
rahmu 2012年

2
您可能需要检查if len(sys.argv)>1:而不是if sys.argv[1]:其他方法,否则会得到超出范围错误的索引
Yibo Yang

3

我的首选实现方法是...(这取自一个名为Harbinger's Hollow的不错的Linux小博客)

#!/usr/bin/env python

import argparse, sys

parser = argparse.ArgumentParser()
parser.add_argument('filename', nargs='?')
args = parser.parse_args()
if args.filename:
    string = open(args.filename).read()
elif not sys.stdin.isatty():
    string = sys.stdin.read()
else:
    parser.print_help()

我之所以喜欢这个最好的原因是,正如博客作者所言,如果不加输入而意外调用它只会输出一条愚蠢的消息。它还很好地插入了我所有现有的Python脚本中,以至于我全部修改了它们以使其包含在内。


3
有时,您确实希望从tty交互式输入输入。检查isatty和发布不符合Unix过滤器的原理。
musiphil

除了isatty疣,这涵盖了其他答案中未找到的有用和重要的方面,因此得到了我的认可。
人间

3
files=sys.argv[1:]

for f in files or [sys.stdin]:
   if isinstance(f, file):
      txt = f.read()
   else:
      txt = open(f).read()

   process(txt)

如果/dev/stdin在我的所有系统上均不可用,这就是我的编写方式。
Mikel

0

我正在使用此解决方案,它就像一个魅力。实际上,我在脚本中使用unaccent,该脚本将小写并从给定字符串中删除重音

argument = sys.argv[1:] if len(sys.argv) > 1 else sys.stdin.read()

我想我看到这个解决方案的最火的时候是在这里


0

如果您的系统没有/dev/stdin,或者您想要一个更通用的解决方案,则可以尝试更复杂的方法,例如:

class Stdin(object):
    def __getattr__(self, attr):
        return getattr(sys.stdin, attr)

    def __enter__(self):
        return self

def myopen(path):
    if path == "-":
        return Stdin()
    return open(path)

for n in sys.argv[1:] or ["-"]:
    with myopen(n) as f:
            ...

为什么在退出时移动文件指针?馊主意。如果输入是从文件重定向的,则下一个程序将再次读取它。(如果stdin是一个终端,seek通常什么也不做,对吗?)就别管它了。
Alexis

是的,完成了。我只是认为-多次使用很可爱。:)
Mikel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.