我正在翻译一个bash脚本,用于运行计算和对Python进行一些后处理,当我尝试将程序输出传递到sed时遇到了麻烦。问题围绕翻译这种管道进行:
#!/bin/bash
echo -e "whatever\n1 2" | ./a.out | sed -e 's/.* //'
可执行文件的编译位置:
#include <iostream>
#include <string>
#define FLUSH true
int main(int argc, char** argv) {
std::string filename;
int param1, param2;
std::cout << "Input name of file: " << std::flush;
std::cin >> filename;
std::cout << "Enter params, separated by a space: " << std::flush;
std::cin >> param1 >> param2;
for(int i = 0; i < 400; i++) {
std::cout << "Result " << i << ": " << i*param1+i*i*param2/(param1+i) << "\n";
if(FLUSH) {
std::cout << std::flush;
}
}
}
我尝试以下
import subprocess
compute = subprocess.Popen(['./a.out'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
strip = subprocess.Popen(['sed','-e', 's/.* //'], stdin=compute.stdout, stdout=subprocess.PIPE)
out, err = compute.communicate('filename.csv\n1 2')
print out
但是我没有得到预期的原始输出列表,而是从程序输出中获得了行的随机子序列,例如:
Result 0: 0
Result 2: 4
Result 3: 7
Result 4: 10
Result 5: 13
Result 6: 16
Result 14: 40
...
Result 392: 1174
Result 396: 1186
Result 399: 1195
我不是用随机来表示任意:输出从脚本的一次执行到下一次执行都不同。因此,我认为问题与输出缓冲区刷新的时间有关,因此sed一次不会接收一条输出线-这就是为什么我尝试在每行之后可变地刷新缓冲区。但是,这并不能解决问题,我在网上找到的有关使用子流程执行此类任务的示例都没有提到此问题。也许有什么方法可以确保第二条命令在第一个命令终止之前不运行,还是一次处理一行?
第二个问题是为什么在我的玩具示例中sed脚本实际上并未真正执行所需的子字符串删除;尽管我没有在整个管道中传递随机字符串,但它在我编写的真实脚本中仍然可以正常工作。
需要明确的是,所需的输出如下所示:
0
2
4
7
10
13
16
19
...
1183
1186
1189
1192
1195
答案 0 :(得分:1)
输出是不确定的,因为两个进程正在从同一文件(管道)sed
和您的驱动程序中同时读取。您看到的输出就是您的驱动程序获得的输出,因为您甚至从未读过strip.stdout
,这就是为什么它没有得到处理的原因。
因此communicate
在这里不起作用,这很不幸,因为如果compute.stdin
等待您耗尽其输出,则手动写入sed
将死锁管。 (输入的内容很少,但不会发生,但总体上来说并不安全。)您可以通过在调用Popen
之前将一个communicate
对象的流分配给另一个select
对象来作弊。另一种简单的方法是使用另一个线程编写输入。还有其他方法,例如asyncio
或它的包装器(或类似的多路复用器),例如communicate
。
当然,您也可以一次>>> x = input("-->")
-->a\nb
>>> x
'a\\nb'
>>> print(x)
a\nb
>>> y = "a\nb"
>>> y
'a\nb'
>>> print(y)
a
b
一次处理一个进程(缓冲所有内容),或者只在自己的进程中进行字符串修整,但是我想您希望自由地通过一个不那么琐碎的进程进行过滤。 / p>