如何使用python执行'<(cat fileA fileB)'?

时间:2014-10-27 17:25:26

标签: python shell

我正在编写一个使用其他软件的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哪部分我做错了?

如果没有中间文件会更好。出于这个原因,我正在寻找命名管道。我并不安静地理解它。

我已经看过这里已回答的多个问题。对我来说,他们与我的问题有些不同。 谢谢你的帮助。

4 个答案:

答案 0 :(得分:3)

bash处理<(..)的方式是:

  1. 创建管道
  2. 分叉写入写入端的命令
  3. <(..)替换为/ dev / fd / N,其中N是管道的输入结束文件描述符(try echo <(true))。
  4. 运行命令
  5. 然后该命令将打开/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脚本中添加更多有趣的逻辑,你是否如此倾向。