20

我有一个 CLI 脚本并希望它从文件中读取数据。它应该能够以两种方式阅读它:

  • cat data.txt | ./my_script.py
  • ./my_script.py data.txt

——例如,有点像grep

我知道的:

  • sys.argvoptparse让我轻松阅读任何参数和选项。
  • sys.stdin让我读取管道输入的数据
  • fileinput使整个过程自动化

很遗憾:

  • usingfileinput使用标准输入和任何参数作为输入。所以我不能使用不是文件名的选项,因为它试图打开它们。
  • sys.stdin.readlines()工作正常,但如果我不传输任何数据,它会挂起,直到我输入Ctrl + D
  • 我不知道如何实现“如果标准输入中没有任何内容,则从 args 中的文件中读取”,因为stdin它始终True处于布尔上下文中。

如果可能的话,我想要一种便携的方式来做到这一点。

4

6 回答 6

20

Argparseoptparse允许以相当简单的方式完成此操作,除非您遇到兼容性问题,否则您确实应该使用它而不是。

代码会是这样的:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--input', type = argparse.FileType('r'), default = '-')

现在您有了一个解析器,它将解析您的命令行参数,如果它看到一个文件,则使用一个文件,如果没有,则使用标准输入。

于 2010-02-15T10:26:15.570 回答
12

随心所欲地处理非文件名参数,最后得到一个非选项参数数组,然后将该数组作为参数传递给fileinput.input()

import fileinput
for line in fileinput.input(remaining_args):
    process(line)
于 2010-02-15T10:32:30.960 回答
9

对于 unix/linux,您可以通过查看来检测是否正在通过管道输入数据os.isatty(0)

$ date | python -c "import os;print os.isatty(0)"
False
$ python -c "import os;print os.isatty(0)"
True

我不确定是否有适用于 Windows 的等价物。

编辑 好的,我在windows XP上用python2.6试过了

C:\Python26>echo "hello" | python.exe -c "import os;print os.isatty(0)"  
False

C:\Python26> python.exe -c "import os;print os.isatty(0)"  
True

所以也许这对 Windows 来说并非毫无希望

于 2010-02-15T10:00:54.570 回答
4

我是菜鸟,所以这可能不是一个好的答案,但我正在尝试做同样的事情(在命令行上允许一个或多个文件,否则默认为 STDIN)。

我整理的最后一个组合:

parser = argparse.ArgumentParser()
parser.add_argument("infiles", nargs="*")
args = parser.parse_args()

for line in fileinput.input(args.infiles):
    process(line)

这似乎是在一个优雅的包中获得所有所需行为的唯一方法,而不需要命名参数。就像使用 unix 命令一样:

cat file1 file2
wc -l < file1

不是:

cat --file file1 --file file2

非常感谢资深惯用 Pythonistas 的反馈/确认,以确保我得到最佳答案。还没有在其他任何地方看到过这个完整的解决方案,只是片段。

于 2013-02-01T19:33:31.327 回答
3

没有可靠的方法来检测是否sys.stdin连接到任何东西,这样做也不合适(例如,用户想要粘贴数据)。检测文件名是否存在作为参数,如果没有找到,则使用标准输入。

于 2010-02-15T09:46:03.550 回答
2

您可以使用此函数来检测输入是否来自管道。

sys.stdin.isatty()

如果输入来自管道,则返回 false,否则返回 true。

于 2012-07-11T11:32:10.123 回答