如何在指定秒数后中断MATLAB系统调用

9o685dep  于 2023-02-05  发布在  Matlab
关注(0)|答案(2)|浏览(244)

我正在使用Windows MATLAB运行SSH命令,但SSH命令偶尔会无限期挂起,我的MATLAB脚本也会无限期挂起(有时在通宵运行时)。我如何才能在一定的等待时间后让命令超时?例如,假设我不想等待SSH命令超过3秒才能完成执行,然后中断它并继续:

% placeholder for a command that sometimes hangs
[status,result] = system('ssh some-user@0.1.2.3 sleep 10')
% placeholder for something I still want to run if the above command hangs
[status,result] = system('ssh some-user@0.1.2.3 ls')

我想创建一个函数sys_with_timeout,它可以像这样使用:

timeoutDuration = 3;
[status,result] = sys_with_timeout('ssh some-user@0.1.2.3 sleep 10', timeoutDuration)
[status,result] = sys_with_timeout('ssh some-user@0.1.2.3 ls', timeoutDuration)

我已经尝试了timeout function from FEX,但它似乎不适用于system/SSH命令。

yk9xbfzb

yk9xbfzb1#

我不认为system命令是非常灵活的,我也不认为有什么方法可以用它来做你想做的事情,但是我们可以使用内置在MATLAB中的Java功能来做这件事,根据this answer by André Caron
下面是等待进程超时完成的方法:

runtime = java.lang.Runtime.getRuntime();
process = runtime.exec('sleep 20');
process.waitFor(10, java.util.concurrent.TimeUnit.SECONDS);
if process.isAlive()
    disp("Done waiting for this...")
    process.destroyForcibly();
end
process.exitValue()

在本例中,我们运行sleep 20 shell命令,然后waitFor()等待程序完成,但最长等待10秒。我们轮询进程是否仍在运行,如果仍在运行,则终止它。exitValue()返回状态(如果需要)。
运行sleep 5时,我看到:

ans =
     0

运行sleep 20时,我看到:

Done waiting for this...
ans =
   137
wsxa1bj1

wsxa1bj12#

基于@Cris Luengo的答案,这里是sys_with_timeout()函数。我没有使用process.waitFor()函数,因为我更愿意在while循环中等待,并在命令输入时显示输出。while循环在命令完成或超时时中断,以先发生者为准。

function [status,cmdout] = sys_with_timeout(command,timeoutSeconds,streamOutput,errorOnTimeout)
    arguments
        command char
        timeoutSeconds {mustBeNonnegative} = Inf
        streamOutput logical = true    % display output as it comes in
        errorOnTimeout logical = false % if false, display warning only
    end
    % launch command as java process (does not wait for output)
    process = java.lang.Runtime.getRuntime().exec(command);
    % start the timeout timer!
    timeoutTimer = tic();
    % Output reader (from https://www.mathworks.com/matlabcentral/answers/257278)
    outputReader = java.io.BufferedReader(java.io.InputStreamReader(process.getInputStream()));
    errorReader = java.io.BufferedReader(java.io.InputStreamReader(process.getErrorStream()));

    % initialize output char array
    cmdout = '';
    while true
        % If any lines are ready to read, append them to output
        % and display them if streamOutput is true
        if outputReader.ready() || errorReader.ready()
            if outputReader.ready()
                nextLine = char(outputReader.readLine());
            elseif errorReader.ready()
                nextLine = char(errorReader.readLine());
            end
            cmdout = [cmdout,nextLine,newline()];
            if streamOutput == true
                disp(nextLine);
            end
            continue
        else
            % if there are no lines ready in the reader, and the
            % process is no longer running, then we are done
            if ~process.isAlive()
                break
            end
            % Check for timeout.  If timeout is reached, destroy
            % the process and break
            if toc(timeoutTimer) > timeoutSeconds
                timeoutMessage = ['sys_with_timeout(''',command, ''',', num2str(timeoutSeconds), ')',...
                                  ' failed after timeout of ',num2str(toc(timeoutTimer)),' seconds'];
                if errorOnTimeout == true
                    error(timeoutMessage)
                else
                    warning(timeoutMessage)
                end
                process.destroyForcibly();
                break
            end
        end
    end
    if ~isempty(cmdout)
        cmdout(end) = []; % remove trailing newline of command output
    end
    status = process.exitValue(); % return
end

为了简单起见,将ssh some-user@0.1.2.3替换为wsl(当然需要WSL),下面是一个超时函数的示例:

>> [status,cmdout] = sys_with_timeout('wsl echo start! && sleep 2 && echo finished!',1)
start!
Warning: sys_with_timeout('wsl echo start! && sleep 2 && echo finished!',1) failed after timeout of 1.0002 seconds 
> In sys_with_timeout (line 41) 
status =
     1
cmdout =
    'start!'

这是一个不会超时的函数的例子

>> [status,cmdout] = sys_with_timeout('wsl echo start! && sleep 2 && echo finished!',3)
start!
finished!
status =
     0
cmdout =
    'start!
     finished!'

相关问题