将shell命令的输出导入到Python脚本中

52

我想运行一个 mysql 命令,并将输出作为变量在我的 Python 脚本中使用。

这是我要执行的 shell 命令:

$ mysql my_database --html -e "select * from limbs" | ./script.py

这是 Python 脚本:

#!/usr/bin/env python

import sys

def hello(variable):
    print variable

我该如何在Python脚本中接收变量并打印输出?

7个回答

62

你需要从标准输入读取数据到Python脚本中,例如:

#!/usr/bin/env python

import sys

def hello(variable):
    print variable

data = sys.stdin.read()
hello(data)

如果你想要做的仅仅是从MySQL数据库中获取一些数据,然后使用Python进行操作,我建议跳过将其导入脚本的步骤,直接使用Python MySql模块执行SQL查询。


2
似乎 shebang 很重要,这就是为什么其他示例都无法工作的原因。如果没有它,程序将永远挂起。 - Antimony

36

如果你想让你的脚本像许多Unix命令行工具一样接受管道或文件名作为第一个参数, 你可以使用以下代码:

#!/usr/bin/env python
import sys

# use stdin if it's full                                                        
if not sys.stdin.isatty():
    input_stream = sys.stdin

# otherwise, read the given filename                                            
else:
    try:
        input_filename = sys.argv[1]
    except IndexError:
        message = 'need filename as first argument if stdin is not full'
        raise IndexError(message)
    else:
        input_stream = open(input_filename, 'rU')

for line in input_stream:
    print(line) # do something useful with each line

16

当你将一个命令的输出管道传递给Python脚本时,它会进入sys.stdin。你可以像读取文件一样从sys.stdin中读取数据。例如:

import sys

print sys.stdin.read()

这个程序字面上输出其输入。


2
+1 是指“就像文件一样”,希望这会让 OP 意识到他也可以做一些类似“for line in sys.stdin:”之类的事情。 - abarnert

9

由于在搜索“将数据导入Python脚本”时,此答案会在Google的首位出现,因此我想添加另一种方法,这种方法我在搜索比使用sys更少“磨砂”的方法时在[J. Beazley的Python Cookbook][1]中找到。在我看来,这种方法更具Python风格,即使对新用户也很容易理解。

import fileinput
with fileinput.input() as f_input:
    for line in f_input:
        print(line, end='')

这种方法也适用于像这样结构化的命令:
$ ls | ./filein.py          # Prints a directory listing to stdout.
$ ./filein.py /etc/passwd   # Reads /etc/passwd to stdout.
$ ./filein.py < /etc/passwd # Reads /etc/passwd to stdout.

如果您需要更复杂的解决方案,您可以结合argparsefileinput [如martinth的此代码片段所示][2]:
import argparse
import fileinput

if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument('--dummy', help='dummy argument')
    parser.add_argument('files', metavar='FILE', nargs='*', help='files to read, if empty, stdin is used')
    args = parser.parse_args()

    # If you would call fileinput.input() without files it would try to process all arguments.
    # We pass '-' as only file when argparse got no files which will cause fileinput to read from stdin
    for line in fileinput.input(files=args.files if len(args.files) > 0 else ('-', )):
        print(line)



  [1]: https://library.oreilly.com/book/0636920027072/python-cookbook-3rd-edition/199.xhtml?ref=toc#_accepting_script_input_via_redirection_pipes_or_input_files
  [2]: https://gist.github.com/martinth/ed991fb8cdcac3dfadf7

使用此方法(使用Python 2.7),出现错误:Traceback (most recent call last): File "./pipetome.py", line 4, in with fileinput.input() as f_input: AttributeError: FileInput实例没有属性'__exit__' - G.A.
以上代码仅在Python3中测试过 - 也许可以尝试使用f_input = fileinput.input()代替with - deepbrook
但是如何区分参数和标准输入,例如 send_mail -to xxx@xxx.com -subject 'passwd' < /etc/passwd - zhuguowei
我已经更新了答案,并提供了一个解决你所述问题的示例。 - deepbrook
在读取管道文件之前,我必须使用 line.strip() 进行处理。 - Shoan
1
FYI:第一行应该是import argparse而不是argpase;-) - Glubbdrubb

8

你可以使用命令行工具xargs

echo 'arg1' | xargs python script.py

现在可以通过script.py文件中的sys.argv[1]获得arg1.


这似乎无法处理多行管道内容。 - npit
@ZephaniahGrunschlag 我认为这与提供额外的CLI参数是相同的,例如:python script.py "gday"。一个不依赖于 xargs 的一行代码会很好。 - not2qubit
我认为我的回答脱离了上下文,所以我删除了它。 - Zephaniah Grunschlag
如何将输入逐行从Linux程序传输到Python中讨论了使用xargs方法的缺点。 - user2514157

3

适用于WindowsPython 3.10.3的单行代码是使用sys.stdin.read(),如下所示:

echo 'Hello!' | python -c "import sys;d=sys.stdin.read(); print('{}\n'.format(d))"

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接