如何使python脚本在bash和python

时间:2015-12-25 01:19:06

标签: python pipeline python-multithreading

摘要:我想在命令行上编写类似于bash脚本的python脚本,但是我也想在python中轻松地将它们连接在一起。我遇到麻烦的地方就是制造后者的粘合剂。

想象一下,我写了两个脚本script1.pyscript2.py,我可以将它们组合在一起:

echo input_string | ./script1.py -a -b | ./script2.py -c -d

如何从另一个python文件中获取此行为? 这就是我所知道的方式,但我不喜欢:

arg_string_1 = convert_to_args(param_1, param_2)
arg_string_2 = convert_to_args(param_3, param_4)
output_string = subprocess.check_output("echo " + input_string + " | ./script1.py " + arg_string_1 + " | ./script2.py " + arg_string_2)

如果我不想利用多线程,我可以做这样的事情(?):

input1  = StringIO(input_string)
output1 = StringIO()
script1.main(param_1, param_2, input1, output1)
input2  = StringIO(output1.get_value())
output2 = StringIO()
script2.main(param_3, param_4, input2, output2)

这是我尝试的方法,但是我一直在写胶水。我很高兴学习如何完成下面的方法,或建议更好的设计/方法!

我的方法:我写了script1.pyscript2.py看起来像:

#!/usr/bin/python3

... # import sys and define "parse_args"

def main(param_1, param_2, input, output):
   for line in input:
     ...
     print(stuff, file=output)

if __name__ == "__main__":
  parameter_1, parameter_2 = parse_args(sys.argv)
  main(parameter_1, parameter_2, sys.stdin, sys.stdout)

然后我想写这样的东西,但不知道如何完成:

pipe_out, pipe_in = ????
output = StringIO()
thread_1 = Thread(target=script1.main, args=(param_1, param_2, StreamIO(input_string), pipe_out))
thread_2 = Thread(target=script2.main, args=(param_3, param_4, pipe_in, output)
thread_1.start()
thread_2.start()
thread_1.join()
thread_2.join()
output_str = output.get_value()

3 个答案:

答案 0 :(得分:1)

对于#34;中的"管道,使用sys.stdin方法readlines()。 (使用方法read()一次只能读取一个字符。)

要将信息从一个线程传递到另一个线程,您可以使用Queue。您必须定义一种方式来表示数据结束。在我的示例中,由于线程之间传递的所有数据都是str,因此我只使用None对象来指示数据的结束(因为它不会出现在传输的数据中)。

还可以使用更多线程,或在线程中使用不同的函数。

我的示例中没有包含sys.argv以保持简单。修改它以获取参数(parameter1,...)应该很容易。

import sys
from threading import Thread
from Queue import Queue
import fileinput

def stdin_to_queue( output_queue ):
  for inp_line in sys.stdin.readlines():     # input one line at at time                                                
    output_queue.put( inp_line, True, None )  # blocking, no timeout
  output_queue.put( None, True, None )    # signal the end of data                                                  


def main1(input_queue, output_queue, arg1, arg2):
  do_loop = True
  while do_loop:
    inp_data = input_queue.get(True)
    if inp_data is None:
      do_loop = False
      output_queue.put( None, True, None )  # signal end of data                                                    
    else:
      out_data = arg1 + inp_data.strip('\r\n').upper() + arg2 #  or whatever transformation...                                    
      output_queue.put( out_data, True, None )

def queue_to_stdout(input_queue):
  do_loop = True
  while do_loop:
    inp_data = input_queue.get(True)
    if inp_data is None:
      do_loop = False
    else:
      sys.stdout.write( inp_data )


def main():
  q12 = Queue()
  q23 = Queue()
  q34 = Queue()
  t1 = Thread(target=stdin_to_queue, args=(q12,) )
  t2 = Thread(target=main1, args=(q12,q23,'(',')') )
  t3 = Thread(target=main1, args=(q23,q34,'[',']') )
  t4 = Thread(target=queue_to_stdout, args=(q34,))
  t1.start()
  t2.start()
  t3.start()
  t4.start()


main()

最后,我用文本文件测试了这个程序(python2)。

head sometextfile.txt | python script.py 

答案 1 :(得分:1)

将返回值重定向到stdout,具体取决于是否从命令行运行脚本:

#!/usr/bin/python3
import sys

# Example function
def main(input):
    # Do something with input producing stuff
    ...
    return multipipe(stuff)

if __name__ == '__main__':
    def multipipe(data):
        print(data)

    input = parse_args(sys.argv)
    main(input)
else:
    def multipipe(data):
        return data

每个其他脚本将具有相同的两个multipipe定义。现在,使用multipipe输出。

如果从命令行$ ./scrip1.py | ./scrip2.py一起调用所有脚本,则每个脚本都会有__name__ == '__main__',因此multipipe会将所有脚本打印到stdout以供下一个读取作为参数脚本(并返回None,因此每个函数都返回None,但在这种情况下你还没有查看返回值。)

如果你在其他python脚本中调用它们,每个函数都会返回你传递给multipipe的任何内容。

有效地,您可以使用现有功能,只需将print(stuff, file=output)替换为return multipipe(stuff)即可。好又简单。

要将它与多线程或多处理一起使用,请将函数设置为使每个函数返回单个函数,并将它们插入到将数据添加到多线程队列的简单函数中。有关此类排队系统的示例,请参阅the sample at the bottom of the Queue docs。通过该示例,只需确保管道中的每个步骤都放置None(或您选择的其他标记值 - 我喜欢...,因为它非常罕见。 d将Ellipsis对象的任何原因(除了作为其单例的标记)传递给队列中的下一个,以表示完成。

答案 2 :(得分:0)

使用标准Popen类有一个非常简单的解决方案。

以下是一个例子:

#this is the master python program
import subprocess
import sys
import os

#note the use of stdin and stdout arguments here
process1 = subprocess.Popen(['./script1.py'], stdin=sys.stdin, stdout=subprocess.PIPE)
process2 = subprocess.Popen(['./script2.py'], stdin=process1.stdout)

process1.wait()
process2.wait()

这两个脚本是:

#!/usr/bin/env python
#script1.py
import sys

for line in sys.stdin:
    print(line.strip().upper())

这是第二个

#!/usr/bin/env python
#script2.py
import sys

for line in sys.stdin:
    print("<{}>".format(line.strip()))