如何使python脚本在bash和python中都可以管道化
摘要:我想在命令行上编写类似bash脚本的python脚本,但是我还想在python中轻松地将它们连接在一起。我遇到的麻烦是胶水使后者发生 想象一下,我写了两个脚本,如何使python脚本在bash和python中都可以管道化,python,pipeline,python-multithreading,Python,Pipeline,Python Multithreading,摘要:我想在命令行上编写类似bash脚本的python脚本,但是我还想在python中轻松地将它们连接在一起。我遇到的麻烦是胶水使后者发生 想象一下,我写了两个脚本,script1.py和script2.py,我可以像这样将它们连接在一起: echo input_string | ./script1.py -a -b | ./script2.py -c -d 如何从另一个python文件中获取此行为? 这是我知道的方式,但我不喜欢: arg_string_1 = convert_to_args
script1.py
和script2.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)
如果我不想利用多线程,我可以这样做(?):
这是我尝试的方法,但我在写胶水时遇到了麻烦。我很感激学习如何完成下面的方法,或者为更好的设计/方法提出建议 我的方法:我编写了
script1.py
和script2.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()
对于“管道输入”,使用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
根据脚本是否从命令行运行,将返回值重定向到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
一起调用所有脚本,则每个脚本都将具有\uuuu name\uuu=='\uuuu main\uuuu'
,因此多管道
将全部打印到stdout,以供下一个脚本作为参数读取(并返回None
,因此每个函数都返回None
,但在本例中,您不会查看返回值)
如果在其他python脚本中调用它们,则每个函数将返回传递给multipipe
的任何内容
实际上,您可以使用现有的函数,只需将print(stuff,file=output)
替换为returnmultipipe(stuff)
。非常简单
要将其用于多线程或多处理,请设置函数,使每个函数都返回一件事,然后将它们插入到一个简单的函数中,将数据添加到多线程队列中。有关此类排队系统的示例,请参阅。使用该示例,只需确保管道中的每个步骤都将
None
(或者您选择的其他哨兵值-我喜欢…
,因为您很少会因为任何原因将省略号
对象传递到队列中的下一个对象以表示完成。使用标准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()))
!/usr/bin/env python
#脚本2.py
导入系统
对于sys.stdin中的行:
打印(“.”格式(line.strip())
谢谢,这看起来不错,只是代码太多了。我本来希望有更简洁的东西。但是如果性能好的话,这可能是值得的。有很多方法可以减少它。例如,你可以直接从一个文件读取,然后在同一个线程中直接写入一个文件。我特意将很多东西分离到illustr中单独吃了很多东西。谢谢,这肯定比我的解决方案好。它仍然保留处理参数的方法。此解决方案似乎需要获取参数对象,将其转换为字符串,将其传递给Popen,然后process1解析字符串并重新创建对象。直接传递就好了对象通过。@usul参数没有问题,只需将['./script1.py',param1',param2'.
放在对Popen的调用中,而不只是['./script1.py']
Hi@Yoav,是的,如果参数已经是字符串,这很好。但是如果参数是列表或更复杂的对象,就没有那么好了。例如,如果我有一个datetime对象,我必须首先将其转换为字符串,然后传入,然后script1.py必须再次将其解析为datetime对象。@usul,嗯-命令行参数总是strings—这是无法逃避的。如果您想在Python进程之间通信Python对象,有比在命令行上以字符串形式传递它们更好的解决方案。但是,从最初的问题中,我发现您想知道如何进行管道处理—这正是我要解决的问题。如果您知道您正在运行Python进程,例如,你可以在它们之间通过管道传输pickle对象。是的,问题是我也在试着吃我的蛋糕。我想支持从命令行调用脚本(参数是字符串)和从python调用脚本(参数是进程),在这两种情况下我都想通过管道传输信息。
#!/usr/bin/env python
#script2.py
import sys
for line in sys.stdin:
print("<{}>".format(line.strip()))