Подождите, пока внешняя программа закончит запись в файл

У меня есть сценарий 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'

Любой способ дождаться окончания записи файла в tmp_file.name без использования таймера, который ждет фиксированное количество времени. Или есть встроенный способ в sox использовать выходной файл в качестве ввода? Специальное имя файла - не работал на pad а также trim,

2 ответа

Решение

Если sox работает с stdin / stdout:

#!/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)

Если вы не можете сделать это для записи / чтения / из stdout / stdin, тогда вы можете попробовать именованный канал, чтобы избежать ожидания файла:

#!/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)

Вы можете подождать файл, прежде чем он будет доступен для чтения:

handle = open(tmp_file.name)
from subprocess import select
select.select([handle], [], [])
Другие вопросы по тегам