我一直在使用MATLAB' system
命令来获取某些linux命令的结果,如下面的简单示例所示:
[junk, result] = system('find ~/ -type f')
这按预期工作,除非用户同时键入MATLAB的命令窗口。在长find
命令期间,这并不罕见。如果发生这种情况,那么用户的输入似乎与find
命令的结果混淆(然后事情就会中断)。
举个例子,而不是:
/path/to/file/one
/path/to/file/two
/path/to/file/three
/path/to/file/four
我可能会得到:
J/path/to/file/one
u/path/to/file/two
n/path/to/file/three
k/path/to/file/four
为了便于演示,我们可以运行类似:
[junk, result] = system('cat')
在命令窗口中键入内容,然后按CTRL + D关闭流。 result
变量将是您在命令窗口中输入的任何内容。
我是否有更安全的方法从MATLAB调用系统命令而不会有输入损坏的风险?
答案 0 :(得分:4)
哇。这种行为令人惊讶。值得向MathWorks报告错误的声音。我在OS X上测试了它,看到了相同的行为。
作为一种变通方法,您可以使用对system()
的调用以及Matlab中嵌入的JVM中的相关对象重新实现java.lang.Process
。
你需要:
~
和其他变量和通配符的扩展,并支持在Matlab系统的单个字符串中指定命令及其参数。
**或者,如果您想要更低级别的控制并且不想处理shell的转义和引用字符串,则可以公开arguments-array形式。两者都很有用。这是一个例子。
function [status,out,errout] = systemwithjava(cmd)
%SYSTEMCMD Version of system implemented with java.lang features
%
% [status,out,errout] = systemwithcmd(cmd)
%
% Written to work around issue with Matlab UI entry getting mixed up with
% output captured by system().
if isunix
% Use 'sh -s' to enable processing of single line command and expansion of ~
% and other special characters, like the Matlab system() does
pb = java.lang.ProcessBuilder({'bash', '-s'});
% Redirect stdout to avoid filling up buffers
myTempname = tempname;
stdoutFile = [myTempname '.systemwithjava.out'];
stderrFile = [myTempname '.systemwithjava.err'];
pb.redirectOutput(java.io.File(stdoutFile));
pb.redirectError(java.io.File(stderrFile));
p = pb.start();
RAII.cleanUpProcess = onCleanup(@() p.destroy());
RAII.stdoutFile = onCleanup(@() delete(stdoutFile));
RAII.stderrFile = onCleanup(@() delete(stderrFile));
childStdin = java.io.PrintStream(p.getOutputStream());
childStdin.println(cmd);
childStdin.close();
else
% TODO: Fill in Windows implementation here
end
% Poll instead of waitFor() so Ctrl-C stays live
% This try/catch mechanism is lousy, but there is no isFinished() method.
% Could be done more cleanly with a Java worker that did waitFor() on a
% separate thread, and have the GUI event thread interrupt it on Ctrl-C.
status = [];
while true
try
status = p.exitValue();
% If that returned, it means the process is finished
break;
catch err
if isequal(err.identifier, 'MATLAB:Java:GenericException') ...
&& isa(err.ExceptionObject, 'java.lang.IllegalThreadStateException')
% Means child process is still running
% (Seriously, java.lang.Process, no "bool isFinished()"?
% Just continue
else
rethrow(err);
end
end
% Pause to allow UI event processing, including Ctrl-C
pause(.01);
end
% Collect output
out = slurpfile(stdoutFile);
errout = slurpfile(stderrFile);
end
function out = slurpfile(file)
fid = fopen(file, 'r');
RAII.fid = onCleanup(@() fclose(fid));
out = fread(fid, 'char=>char')'; %'
end
我尽可能地尝试了这一点,看起来它保持了子进程的输出与Matlab IDE的键盘输入分开。键盘输入被缓冲并在systemwithjava()
返回后作为附加命令执行。 Ctrl-C保持活动状态并将中断该功能,让子进程被杀死。
答案 1 :(得分:3)
感谢Andrew Janke帮助我找到这个解决方案。
为了轻松重现错误,我们可以运行命令:
[ret, out] = system('sleep 2');
如果我们在运行时输入一些字符,out
变量将会被我们输入的内容所污染。
此问题的解决方案是从/ dev / null重定向stdin,如下所示:
[ret, out] = system('sleep 2 < /dev/null');
这会阻止out
变量被用户输入污染。
有趣的是,虽然这似乎修复了当前MATLAB会话的原始测试用例(在R2014a OSX和R2013b Linux上测试),但如果我们再做[ret, out] = system('sleep 2');
,则输出不再受用户输入的影响。