Подпроцесс Python Модуль: не будет писать .csv, но будет делать это, если команды выполняются индивидуально - PullRequest
1 голос
/ 14 марта 2020

Я работаю над обработкой сигналов и, конечно, работаю в python, но у меня есть некоторый код Matlab, который выполняет важную предварительную обработку. Вместо того, чтобы переписывать в python, я думал, что смогу сделать так, чтобы часть моего конвейера проходила через matlab.

Я пытался сделать это через модуль subprocess python, потому что я знаю, что могу запустить matlab через Терминал.

Мой вопрос:

  • Мой желаемый вывод из кода Matlab - это .csv, который я могу использовать в python. Но почему мой python код ниже не создает .csv? Когда я запускаю команды bash одну за другой в терминале, файл создается. Я также дважды проверил мой синтаксис и скрипты Matlab, и они в порядке.
import subprocess

myBashCommands = [] #we'll populate this list with our desired bash commands to get the smooth PSD.
myBashCommands.append("cd /Applications/MATLAB_R2018b.app/bin") #go inside matlab package
myBashCommands.append("./matlab -nodesktop") #now we're in matlab via terminal
myBashCommands.append("potato_wav = audioread('potato-us.wav')") #create the .wav
myBashCommands.append("[f, t,  psd] = GetSpectrogram.m('potato_wav', 16000, 10)") #compute the spectrogram
myBashCommands.append("smooth_psd = smoothn(10*log(psd), .5)") #smooth it
myBashCommands.append("smooth_psd = SurfaceCubicInterpolator(smooth_psd)") #smooth the surface
myBashCommands.append("dlmwrite('smooth_psd.csv', smooth_psd, 'precision', ',', 'precision', '%.10f')") #overwrite or create a new .csv in the folder

subprocess.run(myBashCommands,stderr=PIPE, stdout=PIPE, shell = True, check = True) #run all of these 

Ничего не возвращается, кроме сообщения об успешном завершении процесса. Если я использую subprocess.call, я получаю 0, что также указывает на успех.

Опять же, когда я запускаю все эти bash команды отдельно в окне терминала fre sh, я получаю .csv успешно.

Есть мысли о том, почему .csv не будет создан при динамическом запуске с подпроцессом? Мне нужно, чтобы это было в произвольных файлах .wav, так что это кажется наименее сложным способом сделать это без создания какого-либо bash скрипта.

1 Ответ

3 голосов
/ 14 марта 2020

subprocess - хитрый инструмент. Первое, что нужно знать, это то, что каждый вызов subprocess.run() будет вызывать один новый процесс. Все остальное будет рассматриваться как аргументы этого нового процесса (argv в C). Запуск кода, который вы разместили, будет переводить на это:

cd /Applications/MATLAB_R2018b.app/bin [lots of other stuff...]

Поскольку cd выполнит свою задачу, используя первый аргумент и игнорируя любые дополнительные аргументы, он успешно завершается и возвращает.

Вот тест, который демонстрирует это более наглядно:

>>> cmds=['cd .. ls who what where when why']
>>> sp.run(cmds, shell=True)
CompletedProcess(args=['cd .. ls who what where when why'], returncode=0)

Если мы добавим ;pwd к концу cmds[0], он покажет один уровень выше того, что показывает os.getcwd(). Но последующие вызовы os.getcwd() покажут то же местоположение, что и первоначально.

Простое решение - разбить все ваши команды на их собственные subprocess.run() вызовы. Но это тоже не сработает. Причина в том, что такие вещи, как cd, не применяются к текущему сеансу, поэтому изменение рабочего каталога таким способом, на который вы надеетесь, не сработает.

Чтобы выполнить sh, используя несколько Для вызовов subprocess.run потребуется комбинация subprocess и некоторых функций в модуле os. В частности, os.chdir(). Но даже тогда вы можете столкнуться с аналогичными проблемами с другими командами и их аргументами.

Быстрый и грязный способ сделать это - собрать все команды вместе, разделенные точкой с запятой, но в виде одной строки (так же, как вы сделали бы в командной строке). Затем выполните его, используя аргумент shell=True для run() (что вы уже делаете). По сути, это:

myBashCommands = ["cd /Applications/MATLAB_R2018b.app/bin;"]
myBashCommands[0] += "./matlab -nodesktop;"
[....]
subprocess.run(myBashCommands,stderr=PIPE, stdout=PIPE, shell = True, check = True) 

Как уже упоминалось в комментариях, здесь есть дополнительный уровень сложности, в котором размещенный код Python фактически содержит встроенный код Matlab. Есть 2 простых способа справиться с этим:

  1. поместить код Matlab в отдельный файл и использовать этот файл в качестве входных данных при вызове matlab. Что-то вроде этого, вероятно, будет работать:
myBashCommands = ["cd /Applications/MATLAB_R2018b.app/bin;"]
myBashCommands[0] += "./matlab -nodesktop run('/path/to/script/file.m');"
subprocess.run(myBashCommands,stderr=PIPE, stdout=PIPE, shell = True, check = True) 
сохраните встроенный код Matlab внутри сценария python (мне это кажется несколько глупым), убедившись, что весь блок правильно заключен в кавычки и экранированы, и передайте его matlab в качестве аргумента командной строки:
myBashCommands = ["cd /Applications/MATLAB_R2018b.app/bin;"]
myBashCommands[0] += "./matlab -nodesktop 'potato_wav = audioread(\'potato-us.wav\'); [f, t,  psd] = GetSpectrogram.m(\'potato_wav\', 16000, 10); smooth_psd = smoothn(10*log(psd), .5); smooth_psd = SurfaceCubicInterpolator(smooth_psd); dlmwrite(\'smooth_psd.csv\', smooth_psd, \'precision\', \',\', \'precision\', \'%.10f\')"
subprocess.run(myBashCommands,stderr=PIPE, stdout=PIPE, shell = True, check = True) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...