python - 子进程 Python 模块:不会编写 .csv,但如果单独运行命令会这样做
问题描述
我正在研究信号处理,当然也在使用 python,但是我有一些 matlab 代码可以进行一些重要的预处理。我认为我可以让我的部分管道通过 matlab 运行,而不是重写为 python。
我已经尝试通过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 命令时,我成功获得了 .csv。
关于为什么在使用子进程动态运行时不会创建 .csv 的任何想法?我需要它来接收任意 .wav 文件,所以这似乎是在不创建某种 bash 脚本的情况下执行此操作的最简单的方法。
解决方案
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
在 末尾添加 a cmds[0]
,它将显示比任何os.getcwd()
显示都高一级。但随后的调用os.getcwd()
将显示与最初相同的位置。
简单的解决方案是将所有命令分解为它们自己的subprocess.run()
调用。但这也不会完全正确。原因是诸如此类的cd
事情不会应用于您当前的会话,因此以您希望的方式更改工作目录是行不通的。
要使用多个subprocess.run
调用来完成此操作,需要组合模块subprocess
中的一些功能os
。具体来说,os.chdir()
。但即便如此,您也可能会遇到与其他命令及其参数类似的问题。
快速而肮脏的方法是将所有命令放在一起,用分号分隔,但作为一个字符串(就像您在命令提示符下所做的那样)。然后,使用 to 的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 代码。有两种简单的方法可以处理:
- 将 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)
推荐阅读
- dropwizard - 是否有 Dropwizard 和 Keycloak 的有效 Oauth2 示例实现?
- json - JQ 从 JSON 文件中提取值并分配给变量
- excel - 使用按钮取消隐藏单元格行
- php - Laravel,“getimagesize(asda.jpg):无法打开流:没有这样的文件或目录”
- c# - 从日期列表中查找最小日期值
- node.js - Jest not showing errors that are thrown
- botframework - 如何创建处理来自应用程序的所有对话框的机器人对象的多个实例
- docker - 在 docker 内构建包后,纱线冻结
- excel - VBA复制链接到列中某个关键字的单元格
- git - TFS CI 构建触发器未触发 SVN 存储库