我正在编写一个使用其他软件的python程序。我能够使用subprocess.popen
传递命令。我正面临一个新问题:我需要将多个文件连接为两个
文件并将它们用作外部程序的输入。命令行如下所示:
extersoftware --fq --f <(cat fileA_1 fileB_1) <(cat fileA_2 fileB_2)
我无法使用shell=True
因为我需要通过变量传递其他命令,例如--fq
。(它们不仅限于--fq,这里只是一个例子)
一种可能的解决方案是生成中间文件。 这就是我的尝试:
file_1 = ['cat', 'fileA_1', 'fileB_1']
p1 = Popen(file_1, stdout=PIPE)
p2 = Popen(['>', 'output_file'], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()
output = p2.communicate()
print output
我收到错误消息:OSError: [Errno 2] No such file or directory
哪部分我做错了?
如果没有中间文件会更好。出于这个原因,我正在寻找命名管道。我并不安静地理解它。
我已经看过这里已回答的多个问题。对我来说,他们与我的问题有些不同。 谢谢你的帮助。
答案 0 :(得分:3)
bash处理<(..)
的方式是:
<(..)
替换为/ dev / fd / N,其中N是管道的输入结束文件描述符(try echo <(true)
)。 然后该命令将打开/dev/fd/N
,操作系统将使其复制管道的继承读取端。
我们可以在Python中做同样的事情:
import subprocess
import os
# Open a pipe and run a command that writes to the write end
input_fd, output_fd = os.pipe()
subprocess.Popen(["cat", "foo.txt", "bar.txt"], shell=False, stdout=output_fd)
os.close(output_fd);
# Run a command that uses /dev/fd/* to read from the read end
proc = subprocess.Popen(["wc", "/dev/fd/" + str(input_fd)],
shell=False, stdout = subprocess.PIPE)
# Read that command's output
print proc.communicate()[0]
例如:
$ cat foo.txt
Hello
$ cat bar.txt
World
$ wc <(cat foo.txt bar.txt)
2 2 12 /dev/fd/63
$ python test.py
2 2 12 /dev/fd/4
答案 1 :(得分:1)
进程替换返回正在使用的设备文件名。您必须将管道传递给更高的FD(例如20),方法是将函数传递给使用preexec_fn
复制它的os.dup2()
,然后传递FD设备文件名(例如/dev/fd/20
)作为电话会议的一个论据。
def assignfd(fd, handle):
def assign():
os.dup2(handle, fd)
return assign
...
p2 = Popen(['cat', '/dev/fd/20'], preexec_fn=assignfd(20, p1.stdout.fileno()))
...
答案 2 :(得分:0)
在这种特定情况下,我们可以使用:
import subprocess
import os
if __name__ == '__main__':
input_fd1, output_fd1 = os.pipe()
subprocess.Popen(['cat', 'fileA_1', 'fileB_1'],
shell=False, stdout=output_fd1)
os.close(output_fd1)
input_fd2, output_fd2 = os.pipe();
subprocess.Popen(['cat', 'fileA_2', 'fileB_2'],
shell=False, stdout=output_fd2)
os.close(output_fd2)
proc = subprocess.Popen(['extersoftware','--fq', '--f',
'/dev/fd/'+str(input_fd1), '/dev/fd/' + str(input_fd2)], shell=False)
更改日志:
重新格式化代码,使它现在应该更容易阅读(并且希望在语法上仍然正确)。它在Scientific Linux 6.5上的Python 2.6.6中进行了测试,一切都很好。
删除了不必要的分号。
答案 3 :(得分:0)
它实际上可能有两种方式 - 使用shell,同时通过明确地传递参数列表,不允许对它们进行shell解析。
明确而不是bash
使用shell=True
以确保您支持<()
,并使用"$@"
来引用其他argv数组元素,如下所示:< / p>
subprocess.Popen(['bash', '-c',
'extersoftware "$@" --f <(cat fileA_1 fileB_1) <(cat fileA_2 fileB_2)',
"_", # this is a dummy passed in as argv[0] of the interpreter
"--fq", # this is substituted into the shell by the "$@"
])
如果你想独立传入所有三个数组 - 额外的参数,以及要传递给每个cat
实例的确切文件名:
BASH_SCRIPT=r'''
declare -a filelist1=( )
filelist1_len=$1; shift
while (( filelist1_len-- > 0 )); do
filelist1+=( "$1" ); shift
done
filelist2_len=$1; shift
while (( filelist2_len-- > 0 )); do
filelist2+=( "$1" ); shift
done
extersoftware "$@" --f <(cat "${filelist1[@]}") <(cat "${filelist2[@]}")
'''
subprocess.Popen(['bash', '-c', BASH_SCRIPT, '' +
[str(len(filelist1))] + filelist1 +
[str(len(filelist2))] + filelist2 +
["--fq"],
])
你可以在嵌入式shell脚本中添加更多有趣的逻辑,你是否如此倾向。