等待外部程序完成文件写入
我有一个Python脚本,它会调用一个外部程序(具体来说是sox
)。现在我需要用sox
做几件事情,但每次都得等一个文件写完了,才能把它作为下一个命令的输入文件。
subprocess.wait()
这个方法不管用,因为sox
虽然执行完了,但文件可能还没写完。
这是我现在的代码:
import tempfile
import shlex
file_url = '/some/file.wav'
out_file = 'some/out.wav'
tmp_file = tempfile.NamedTemporaryFile()
pad_cmd = 'sox ' + file_url + ' ' + tmp_file.name + ' pad 0.0 3.0'
subprocess.call(shlex.split(pad_cmd))
trim_cmd = 'sox ' + tmp_file.name + ' -t wav ' + out_file + ' trim 0.0 3.0'
有没有办法在文件写完之前,不用定时器去等固定的时间?或者sox
有没有内置的方法可以直接把输出文件作为输入?我试过用特殊的文件名-
,但在pad
和trim
命令中没用。
2 个回答
1
你可以等文件准备好后再去读取它:
handle = open(tmp_file.name)
from subprocess import select
select.select([handle], [], [])
2
#!/bin/sh
sox input.wav -t wav - pad 0.0 3.0 | sox -t wav - out.wav trim 0.0 3.0
那么你可以用Python来写:
#!/usr/bin/env python
from subprocess import Popen, PIPE
pad = Popen(["sox", "input.wav", "-t", "wav", "-", "pad", "0.0", "3.0"],
stdout=PIPE)
trim = Popen(['sox', '-t', 'wav', '-', 'out.wav', 'trim', '0.0', '3.0'],
stdin=pad.stdout)
pad.stdout.close() # notify pad on write if trim dies prematurely
pipestatus = [p.wait() for p in [pad, trim]]
注意:如果你使用的输入是可信的,可以把命令写成一行,这样更容易阅读(在这种情况下,命令行会自动创建管道):
#!/usr/bin/env python
from subprocess import check_call
cmd = "sox input.wav - -t wav pad 0.0 3.0 | sox - -t wav out.wav trim 0.0 3.0"
check_call(cmd, shell=True)
如果你无法让程序读写标准输出/输入,那么你可以试试命名管道,这样就不用等文件了:
#!/bin/sh
mkfifo /tmp/sox.fifo
sox /tmp/sox.fifo -t wav out.wav trim 0.0 3.0 & # read from fifo
sox input.wav /tmp/sox.fifo pad 0.0 3.0 # write to fifo
或者用Python写成一样的效果:
#!/usr/bin/env python
from subprocess import Popen, check_call
with named_pipe() as path:
trim = Popen(["sox", path, '-t', 'wav', 'out.wav', 'trim', '0.0', '3.0'])
check_call(["sox", "input.wav", path, "pad", "0.0", "3.0"]) # pad
rc = trim.wait()
其中named_pipe()
的定义是:
import os
from contextlib import contextmanager
from shutil import rmtree
from tempfile import mkdtemp
@contextmanager
def named_pipe():
dirname = mkdtemp()
try:
path = os.path.join(dirname, 'named_pipe')
os.mkfifo(path)
yield path
finally:
rmtree(dirname)